Compare commits

..

1 Commits

Author SHA1 Message Date
peterlu14 53d4625cb0 [remove] pyc file untracked 2022-03-14 18:05:33 +08:00
60 changed files with 3939 additions and 13671 deletions
-3
View File
@@ -1,10 +1,7 @@
.DS_Store .DS_Store
*.cpython-37.opt-2.pyc *.cpython-37.opt-2.pyc
*.cpython-39-arm-linux-gnueabihf.so
*.
*.pyc *.pyc
*/__pycache__ */__pycache__
/.vscode /.vscode
/media /media
python/biopro/sever/_identify.py python/biopro/sever/_identify.py
*.log
+29 -194
View File
@@ -4,7 +4,7 @@ New goal on pi4 without kotlin and django, and with only one dataserver.
# Installation # Installation
### Get Raspbrain into SD card ### Get Raspbrain into SD card
###### Download Raspbian Bullseye Lite ###### Download Raspbian Buster Lite
>https://www.raspberrypi.org/downloads/raspberry-pi-os/ >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 lsblk
# copy data to sd card # copy data to sd card
sudo dd bs=4M if=yyyy-mm-dd-raspios-bullseye-armhf-lite.img of=/dev/sdX conv=fsync status=progress sudo dd bs=4M if=yyyy-mm-dd-raspbian-buster.img of=/dev/sdX conv=fsync status=progress
# enable ssh on first boot # enable ssh on first boot
add ssh.txt in boot folder add ssh.txt in boot folder
@@ -77,26 +77,19 @@ add ssh.txt in boot folder
###### Install dependencies ###### Install dependencies
sudo apt-get install vim git wiringpi build-essential libi2c-dev i2c-tools python-dev libffi-dev python3-pip postgresql npm sudo apt-get install vim git wiringpi build-essential libi2c-dev i2c-tools python-dev libffi-dev python3-pip postgresql
sudo apt-get install libatlas-base-dev
sudo apt-get install python3-numpy
###### wiringpi conflict
need to install wiringpi: https://github.com/WiringPi/WiringPi
install instructions: https://github.com/WiringPi/WiringPi/blob/master/INSTALL
### Setup project ### Setup project
###### Clone data server project ###### Clone project
``` ```
cd ~ cd ~
git clone https://gitlab.com/wisetop/controller/wisetopdataserver.git git clone https://gitlab.com/bioproscientific/bioprorelife.git
``` ```
###### Install python dependencies ###### Install python dependencies
``` ```
cd ~/wisetopdataserver cd ~/bioprorelife
pip3 install -r requirements.txt pip3 install -r requirements.txt
pip3 install SQLAlchemy
``` ```
##### Packages version ##### Packages version
``` ```
@@ -119,14 +112,14 @@ psycopg2 2.8.5
###### Build C extension ###### Build C extension
``` ```
cd ~/wisetopdataserver/python/biopro/ext/ cd ~/bioprorelife/python/biopro/ext/
rm -rf *.so build rm -rf *.so build
python3 setup.py build_ext --inplace python3 setup.py build_ext --inplace
``` ```
###### Check setup-uart file ###### Check setup-uart file
``` ```
cd ~/wisetopdataserver/ cd ~/bioprorelife
sudo vim ./run-setup-uart.sh sudo vim ./run-setup-uart.sh
``` ```
##### Modify the code as following ##### Modify the code as following
@@ -156,72 +149,44 @@ sudo raspi-config
reboot reboot
``` ```
###### Set pi password
```
cd ~
passwd
Current password:raspberry
New password:5k6zj454778225
Retype new password:5k6zj454778225
```
###### Auto run program when device start ###### Auto run program when device start
``` ```
sudo vim /etc/rc.local sudo vim /etc/rc.local
``` ```
###### add the code following as: add the code following as:
``` ```
sudo -H -u pi nohup /home/pi/wisetopdataserver/run-data-server.sh > /home/pi/.data_server.out & sudo -H -u pi nohup /home/pi/bioprorelife/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 ### MQTT broker install
###### 1.Install mosquitto 1. Install mosquitto
```
sudo apt-get install mosquitto mosquitto-clients sudo apt-get install mosquitto mosquitto-clients
```
2. Enable Remote Access
###### 2.Enable Remote Access ```
sudo vim /etc/mosquitto/conf.d/default.conf sudo vim /etc/mosquitto/conf.d/default.conf
```
add the code following as: add the code following as:
``` ```
listener 1883 listener 1883
protocol mqtt protocol mqtt
listener 8083 listener 8083
protocol websockets protocol websockets
allow_anonymous true
``` ```
###### 3.restart mqtt broker restart mqtt broker
```
sudo systemctl restart mosquitto sudo systemctl restart mosquitto
```
###### 4.Robust MQTT 3. Robust MQTT
create mosquitto_restart.sh create mosquitto_restart.sh
```
cd ~ cd ~
sudo vim mosquitto_restart.sh sudo vim mosquitto_restart.sh
```
add the code following as: add the code following as:
``` ```
if [ "`ps -aux | grep /usr/sbin/mosquitto | wc -l`" == "1" ] if [ "`ps -aux | grep /usr/sbin/mosquitto | wc -l`" == "1" ]
@@ -235,155 +200,25 @@ fi
echo "$SERVICE is currently running" >> /home/ubuntu/cron.log echo "$SERVICE is currently running" >> /home/ubuntu/cron.log
exit 0 exit 0
``` ```
###### 5.add mosquitto_restart.sh to root routing add mosquitto_restart.sh to root routing
```
sudo chmod +x mosquitto_restart.sh sudo chmod +x mosquitto_restart.sh
sudo -i sudo -i
crontab -e crontab -e
```
add the code following as: add the code following as:
``` ```
*/5 * * * * /home/pi/mosquitto_restart.sh */5 * * * * /home/pi/mosquitto_restart.sh
``` ```
###### 6.Auto run MQTT broker when when pi-start 3. Auto run MQTT broker when when pi-start
```
sudo vim /etc/rc.local sudo vim /etc/rc.local
```
add the code following as: add the code following as:
``` ```
sudo nohup systemctl restart mosquitto sudo nohup systemctl restart mosquitto
``` ```
### Setting up a Routed Wireless Access Point ### Setup 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
### Install Apache2
sudo apt install apache2
### Deploy web site
sudo vim /etc/apache2/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: ##### raspberry website document:
> https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md > https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md
# ChangeLog
[2022.12.28]
change meta_project_info to project_metas
change project_report to project_reports
alter table project_meta column cycle type string to jsonb
[2022.11.16]
add python package SQLAlchemy
add new column project(int4) in postgresSQL table recording_meta_datas
add new postgresSQL table project_report
add new postgresSQL table meta_project_info
[2022.10.18]
socektTimeout from 1s -> 5s
socketListen from 3 -> 20
[2022.09.30]
add new column cycle(jsonb) in postgresSQL table projects
[2022.9.28]
add python package numpy
+3 -4
View File
@@ -18,10 +18,9 @@ class ControllerAPI():
@staticmethod @staticmethod
def getByMac(mac_address): def getByMac(mac_address):
try: try:
if mac_address != None: ret = requests.get(API_URL + 'api/controller/get_by_mac/' + mac_address, headers= AuthAPI.get_key())
ret = requests.get(API_URL + 'api/controller/get_by_mac/' + mac_address, headers= AuthAPI.get_key()) if ret.status_code == 200:
if ret.status_code == 200: return ret.json()
return ret.json()
except (requests.exceptions.ConnectionError, json.decoder.JSONDecodeError) as e: except (requests.exceptions.ConnectionError, json.decoder.JSONDecodeError) as e:
print('get controller fail', e) print('get controller fail', e)
return [] return []
-17
View File
@@ -1576,24 +1576,7 @@ class ControlAPI(metaclass=Router):
should handle this exit code and re-run server process. should handle this exit code and re-run server process.
""" """
raise NotImplementedError() raise NotImplementedError()
def run_project(self, project) -> bool:
raise NotImplementedError()
def get_running_project(self) -> bool:
raise NotImplementedError()
def stop_project(self, project) -> bool:
raise NotImplementedError()
def set_project(self, project, content) -> bool:
raise NotImplementedError()
def set_project_cycle(self, project, index, content) -> bool:
raise NotImplementedError()
def show_device_data(self, device) -> bool:
raise NotImplementedError()
# noinspection PyAbstractClass # noinspection PyAbstractClass
class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)): class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)):
+3 -16
View File
@@ -87,8 +87,7 @@ class DataAPI(metaclass=abc.ABCMeta):
def update_device_configuration(self, def update_device_configuration(self,
device: Union[int, Device], device: Union[int, Device],
meta_file: Union[str, Path, RecordingMetaFile], meta_file: Union[str, Path, RecordingMetaFile],
data_format: Union[AnyStr, DataDecodeFormat], data_format: Union[AnyStr, DataDecodeFormat]):
project_info: str):
"""set device configuration. """set device configuration.
:param device: device ID :param device: device ID
@@ -131,14 +130,6 @@ class DataAPI(metaclass=abc.ABCMeta):
def hardware_test(self) -> JSON_OBJECT: def hardware_test(self) -> JSON_OBJECT:
"""""" """"""
pass pass
@abc.abstractmethod
def show_data(self, device: Union[int, Device]):
"""show device data
:param device: device ID
"""
pass
# noinspection PyAbstractClass # noinspection PyAbstractClass
@@ -155,8 +146,7 @@ class DataClient(SocketClient, DataAPI, metaclass=SocketClientMacro(DataAPI)):
def update_device_configuration(self, def update_device_configuration(self,
device: Union[int, Device], device: Union[int, Device],
meta_file: Union[str, Path, RecordingMetaFile], meta_file: Union[str, Path, RecordingMetaFile],
data_format: Union[AnyStr, DataDecodeFormat], data_format: Union[AnyStr, DataDecodeFormat]):
project_info: str):
# meta file # meta file
if isinstance(meta_file, str): if isinstance(meta_file, str):
@@ -188,7 +178,7 @@ class DataClient(SocketClient, DataAPI, metaclass=SocketClientMacro(DataAPI)):
raise TypeError('not a data decode format : ' + data_format.__class__.__name__) raise TypeError('not a data decode format : ' + data_format.__class__.__name__)
# send # send
self.send_command('update_device_configuration', device, meta_file, data_format, project_info) self.send_command('update_device_configuration', device, meta_file, data_format)
def is_sync(self, device: Union[None, int, Device] = None) -> bool: def is_sync(self, device: Union[None, int, Device] = None) -> bool:
if isinstance(device, Device): if isinstance(device, Device):
@@ -201,9 +191,6 @@ class DataClient(SocketClient, DataAPI, metaclass=SocketClientMacro(DataAPI)):
def stop_sync(self, *device: Union[int, Device]): def stop_sync(self, *device: Union[int, Device]):
self.send_command('stop_sync', *self._to_device_id(*device)) self.send_command('stop_sync', *self._to_device_id(*device))
def show_data(self, device: int):
self.send_command('show_data', device)
@staticmethod @staticmethod
def _to_device_id(*device: Union[int, Device]) -> Tuple[int, ...]: def _to_device_id(*device: Union[int, Device]) -> Tuple[int, ...]:
-10
View File
@@ -1,10 +0,0 @@
from sqlalchemy import create_engine
# from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, declarative_base
SQLALCHEMY_DATABASE_URL = "postgresql://biopro:BioProControlBox@127.0.0.1:5432/postgres"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
Session = sessionmaker(bind=engine)
Base = declarative_base()
-60
View File
@@ -1,60 +0,0 @@
from sqlalchemy import Table, Column, String, MetaData, ForeignKey, JSON
from sqlalchemy.sql import select, func
from sqlalchemy.types import Integer, BigInteger, String, Boolean, TIMESTAMP, Numeric
from sqlalchemy.dialects.postgresql import JSONB
from biopro.db.base import Session
from .base import Base
class Collection(Base):
__tablename__ = "collections"
id = Column(Integer, primary_key=True)
name = Column(String(255))
parent = Column(JSONB)
controller_id = Column(Integer)
type = Column(String(255))
description = Column(String(255))
deleted = Column(Boolean)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), onupdate=func.now())
@classmethod
def create_collection(cls, collection_name, parent):
with Session() as session:
name = cls.check_name_duplicate(collection_name, parent, 0)
collection = Collection(
name = name,
parent = parent,
type= "folder",
)
session.add(collection)
session.commit()
print('a', collection.id)
return collection
@classmethod
def check_name_duplicate(cls, collection_name, parent, n):
with Session() as session:
result = session.query(Collection).filter(Collection.name == cls.generate_name(collection_name, n), Collection.parent == parent).first()
if result is None:
return cls.generate_name(collection_name, n)
else:
new_num = n + 1
# new_name = f"{collection_name}({new_num})"
return cls.check_name_duplicate(collection_name, parent, new_num)
@classmethod
def generate_name(cls, collection_name, n):
if n==0:
return collection_name
else:
return f"{collection_name}({n})"
@classmethod
def find_collection(cls, collection_name, parent):
with Session() as session:
result = session.query(Collection).filter(Collection.name == collection_name, Collection.parent == parent).first()
return result
-51
View File
@@ -1,51 +0,0 @@
from sqlalchemy import Table, Column, String, MetaData, ForeignKey, JSON
from sqlalchemy.sql import select, func
from sqlalchemy.types import Integer, BigInteger, String, Boolean, TIMESTAMP, Numeric, Float, BINARY, LargeBinary
from sqlalchemy.dialects.postgresql import JSONB
from .base import Base, Session
class Device(Base):
__tablename__ = "devices"
id = Column(Integer, primary_key=True)
name = Column(String(255))
mac_address = Column(String(255))
serial_number = Column(String(255))
configuration = Column(JSONB)
library = Column(String(255))
library_version = Column(String(255))
device_version = Column(String(255))
type = Column(String(255))
battery = Column(Integer)
temperature = Column(Float)
auto_connect = Column(Boolean)
connect_priority = Column(Integer)
connect_time = Column(BigInteger)
parameter_set = Column(JSONB)
running = Column(Boolean)
calibration = Column(LargeBinary)
calibration_version = Column(Integer, default=-1)
user_auth = Column(JSONB)
deleted = Column(Boolean)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), onupdate=func.now())
@classmethod
def update_device(cls, device, options):
with Session() as session:
result = session.query(Device).filter(Device.mac_address == device['mac_address']).first()
for key, value in options.items():
setattr(result, key, value)
session.commit()
@classmethod
def get_device(cls, device):
with Session() as session:
result = session.query(Device).filter(Device.mac_address == device['mac_address']).first()
return result
# def __repr__(self):
# return f"User(id={self.id!r}, name={self.name!r}, fullname={self.task!r})"
-24
View File
@@ -1,24 +0,0 @@
from sqlalchemy import Table, Column, String, MetaData, ForeignKey, JSON
from sqlalchemy.sql import select, func
from sqlalchemy.types import Integer, BigInteger, String, Boolean, TIMESTAMP, Numeric
from sqlalchemy.dialects.postgresql import JSONB
from .base import Base
class Project(Base):
__tablename__ = "project"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
task = Column(JSONB)
cycle = Column(JSONB)
device = Column(JSONB)
uuid = Column(String(36))
user_auth = Column(JSONB)
deleted = Column(Boolean, default = False)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), onupdate=func.now())
# def __repr__(self):
# return f"User(id={self.id!r}, name={self.name!r}, fullname={self.task!r})"
-29
View File
@@ -1,29 +0,0 @@
from sqlalchemy import Table, Column, String, MetaData, ForeignKey, JSON
from sqlalchemy.sql import select, func
from sqlalchemy.types import Integer, BigInteger, String, Boolean, TIMESTAMP, Numeric
from sqlalchemy.dialects.postgresql import JSONB
from .base import Base, Session
class MetaProjectInfo(Base):
__tablename__ = "project_metas"
id = Column(Integer, primary_key=True)
project = Column(String(36))
cycle = Column(JSONB)
task = Column(JSONB)
serial_number = Column(Integer)
deleted = Column(Boolean, default = False)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), onupdate=func.now())
@classmethod
def create_project_meta(cls, project):
with Session() as session:
project_meta = MetaProjectInfo(project = project['project'], cycle= project['cycle'], task=project['task'], serial_number=int(project['serial_number']))
session.add(project_meta)
session.commit()
return project_meta.id
# def __repr__(self):
# return f"User(id={self.id!r}, name={self.name!r}, fullname={self.task!r})"
-24
View File
@@ -1,24 +0,0 @@
from sqlalchemy import Table, Column, String, MetaData, ForeignKey, JSON
from sqlalchemy.sql import select, func
from sqlalchemy.types import Integer, BigInteger, String, Boolean, TIMESTAMP, Numeric
from sqlalchemy.dialects.postgresql import JSONB
from .base import Base
class ProjectReport(Base):
__tablename__ = "project_reports"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
task = Column(JSONB)
cycle = Column(JSONB)
device = Column(JSONB)
uuid = Column(String(36))
user_auth = Column(JSONB)
deleted = Column(Boolean, default = False)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), onupdate=func.now())
# def __repr__(self):
# return f"User(id={self.id!r}, name={self.name!r}, fullname={self.task!r})"
+374 -362
View File
@@ -350,7 +350,6 @@ class CC2650Device(Device):
self._recording_file_name: str = 'recording_data' self._recording_file_name: str = 'recording_data'
self._coeff: bytes = b'' self._coeff: bytes = b''
self._device_version = "" self._device_version = ""
self._cali_version = -1
@property @property
def device_id(self) -> int: def device_id(self) -> int:
@@ -433,12 +432,12 @@ class CC2650Device(Device):
if length == 1 and instruction[0] < 0: if length == 1 and instruction[0] < 0:
return struct.pack('2B1b', return struct.pack('2B1b',
(ins_type & 0xF0) | (self.device_id & 0x0F), (ins_type & 0xF0) | (self.device_id & 0x0F),
(ins_oper & 0xFF), (ins_oper & 0xF0) | (length & 0x0F),
*instruction) *instruction)
return struct.pack('%dB' % (length + 2), return struct.pack('%dB' % (length + 2),
(ins_type & 0xF0) | (self.device_id & 0x0F), (ins_type & 0xF0) | (self.device_id & 0x0F),
(ins_oper & 0xFF), (ins_oper & 0xF0) | (length & 0x0F),
*instruction) *instruction)
def _decode_data(self, ins_oper: int, data: bytes) -> bytes: def _decode_data(self, ins_oper: int, data: bytes) -> bytes:
@@ -478,24 +477,22 @@ class CC2650Device(Device):
else: else:
if data is not None and len(data) > 0: if data is not None and len(data) > 0:
year = struct.unpack('<B', data[2:3])[0] year = struct.unpack('<B', data[0:1])[0]
month = struct.unpack('<B', data[3:4])[0] month = struct.unpack('<B', data[1:2])[0]
day = struct.unpack('<B', data[4:5])[0] day = struct.unpack('<B', data[2:3])[0]
hour = struct.unpack('<B', data[5:6])[0] hour = struct.unpack('<B', data[3:4])[0]
minute = struct.unpack('<B', data[6:7])[0] minute = struct.unpack('<B', data[4:5])[0]
# mac1 = struct.unpack('<B', data[7:8])[0] mac1 = struct.unpack('<B', data[5:6])[0]
# mac2 = struct.unpack('<B', data[8:9])[0] mac2 = struct.unpack('<B', data[6:7])[0]
# mac1 = "%02X" % mac1 mac1 = "%02X" % mac1
# mac2 = "%02X" % mac2 mac2 = "%02X" % mac2
self._device_version = str(year) + '/' + str(month) + '/' + str(day) + " " + str(hour) + ":" + str( self._device_version = str(year) + '/' + str(month) + '/' + str(day) + " " + str(hour) + ":" + str(
minute) minute) + " | " + str(mac1) + ":" + str(mac2)
# + " | " + str(mac1) + ":" + str(mac2)
@property @property
def battery(self) -> int: def battery(self) -> int:
if self._start_flag == False: self.update_battery_info()
self.update_battery_info()
return self._battery return self._battery
@property @property
@@ -539,8 +536,8 @@ class CC2650Device(Device):
self._master.log_warn('device', self.device_id, 'update_battery_info no response') self._master.log_warn('device', self.device_id, 'update_battery_info no response')
else: else:
if data is not None and len(data) > 2 : if data is not None and len(data) == 4 :
battery = struct.unpack('<H', data[2:4])[0] battery = struct.unpack('<H', data[1:3])[0]
if battery is not None: if battery is not None:
self._battery = battery self._battery = battery
@@ -552,39 +549,6 @@ class CC2650Device(Device):
self.update_calibration_info(device_type) self.update_calibration_info(device_type)
return self._coeff return self._coeff
def _check_crc(self, data):
data_chk_sum = data[-1]
# print('data:', list(data), 'data_chk_sum:', data_chk_sum)
# print('data[0:-1]:', list(data[0:-1]), 'check_sum:', sum(data[0:-1]) & 0b11111111)
if data_chk_sum == sum(data[0:-1]) & 0b11111111:
return True
return False
def update_cali_version(self) -> bool:
for _ in range(5):
try:
code = self._encode_instruction(DeviceInstruction.TYP_CIS, DeviceInstruction.CIS_CALI, 0)
self._master.write_characteristic(self.device_id, CC2650MasterDevice.COMMAND_HANDLE, code)
except SendInstructionTimeoutError:
self._master.log_warn('device', self.device_id, 'update cali version error')
return False
except RuntimeError:
self._master.log_warn('device', self.device_id, 'update cali version error')
return False
else:
version = self._master.read_characteristic(self.device_id,CC2650MasterDevice.RETURN_HANDLE)
if self._check_crc(version):
self._cali_version = struct.unpack('<H', version[2:4])[0]
print('self._cali_version=', self._cali_version)
return True
self._master.log_warn('device', self.device_id, 'update cali version error')
return False
def update_calibration_info(self, device_type: str): def update_calibration_info(self, device_type: str):
""" get device calibration info """ """ get device calibration info """
@@ -607,6 +571,20 @@ class CC2650Device(Device):
elif device_type == 'TDC4VC': elif device_type == 'TDC4VC':
i = 0 i = 0
request_times = 0 request_times = 0
# neulive 2.1 (only support ch1~ch8)
# try:
# # send
# code = self._encode_instruction(DeviceInstruction.TYP_CIS, DeviceInstruction.CIS_CALI, 0)
# self._master.write_characteristic(self.device_id, CC2650MasterDevice.COMMAND_HANDLE, code)
# # receive
# data = self._master.read_characteristic(self.device_id, CC2650MasterDevice.RETURN_HANDLE)
#
# coeff.append(self._decode_data(DeviceInstruction.CIS_CALI, data))
# except SendInstructionTimeoutError:
# self._master.log_warn('device', self.device_id, 'update_calibration_info no response')
# except RuntimeError:
# self._master.log_warn('device', self.device_id, 'update_calibration_info no response')
while i < 4: while i < 4:
try: try:
# print('i', i) # print('i', i)
@@ -682,34 +660,43 @@ class CC2650Device(Device):
break break
elif device_type == 'EISZeroOne': elif device_type == 'EISZeroOne':
i = 1 i = 0
while i <= 24: request_times = 0
for _ in range(5): while i < 7:
try: try:
# send # send
code = self._encode_instruction(DeviceInstruction.TYP_CIS, DeviceInstruction.CIS_CALI, i) code = self._encode_instruction(DeviceInstruction.TYP_CIS, DeviceInstruction.CIS_CALI, i)
self._master.write_characteristic(self.device_id, CC2650MasterDevice.COMMAND_HANDLE, code) self._master.write_characteristic(self.device_id, CC2650MasterDevice.COMMAND_HANDLE, code)
except SerialTimeoutException: sleep(0.1)
self._master.log_warn('device', self.device_id, 'send update_calibration_info instruction fail')
continue # receive
data = self._master.read_characteristic(self.device_id,
CC2650MasterDevice.RETURN_HANDLE)
coeff.append(self._decode_data(DeviceInstruction.CIS_CALI, data))
except SendInstructionTimeoutError as e:
print(e)
self._master.log_warn('device', self.device_id, 'update_calibration_info no response')
raise BaseException
except RuntimeError as e:
print(e)
self._master.log_warn('device', self.device_id, 'update_calibration_info no response - 2')
request_times += 1
if request_times > 3:
self._master.reset(self.device_id)
break
else:
# print('data success')
if len(data) > 0:
i += 1
else: else:
try: request_times += 1
# receive if request_times > 3:
data = self._master.read_characteristic(self.device_id, self._master.reset(self.device_id)
CC2650MasterDevice.RETURN_HANDLE) break
except RecvTimeout:
self._master.log_warn('device', self.device_id, 'update_calibration_info no response')
else:
if self._check_crc(data) and data is not None:
coeff.append(self._decode_data(DeviceInstruction.CIS_CALI, data))
i += 1
else:
self._master.log_warn('device', self.device_id, 'update_calibration_info crc wrong')
continue
else: else:
# default: neulive 2.1 # default: neulive 2.1
for i in range(1): for i in range(1):
@@ -1963,6 +1950,119 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
"""initialize cc2650 (master)""" """initialize cc2650 (master)"""
pass pass
def scan_send_ins(self):
# send scan command
try:
print(':: scan_send_ins ::', self.CC2650_COMMAND_LEN, 3, 0, 0)
self._cc2650.send(self.CC2650_COMMAND_LEN, 3, 0, 0)
except SerialTimeoutException as e:
raise RecvTimeout('send scan fail') from e
else:
# wait scanning
# sleep(2)
clean_buf = self._cc2650.receive_timeout("20B", timeout=3)
print("clean_buf = ", clean_buf)
# def cc2650_uart_irq(self):
# uart_irq = self.get_uart_irq_pin()
# uart_irq.output(False)
# sleep(0.001)
# uart_irq.output(True)
@synchronized
def scan_callback(self, callback: Callable[[DeviceResponseInfo], None], timeout=5, all_device=False) -> bool:
self._found = found = []
self._found_with_id = []
hdr_BPHS = [66, 80, 72, 83]
scan_response: Union[Optional[tuple], Any] = None
# build scan instruction
scan_ins = bytearray()
scan_ins.append(3) #scan instruction
scan_ins.append(1) #length
scan_ins.append(0xF1)
# print('send_scan', bytes(scan_ins))
self._cc2650.send("bytes", bytes(scan_ins))
try:
scan_response = self._cc2650.recv_uart(timeout)
except RecvTimeout:
# self.reset_internal()
# self.reset_hardware()
# self._interface.flush()
return False
# instruction format:
# ins[0]: get_scan_response = 0x04
# ins[1]: number of scanned device=0; a certain device = device_id (could be 1~8)
# ins[2]: addr=MAC=1, localName=2, company_code=3, version_info=4, battery_info=5, all_info=6;
# attr_length=0, e.g. len(addr)=6, len(company_code)=4
local_mac = None
local_cc = None
local_ver = None
local_bat = None
local_name = None
local_addr_type = None
# get device attribute length
attr_length = [6, 4, 6, 5, 20]
index = 0
local_mac = get_device_mac_in_address_format(scan_response[index:index + 6])
index = index + 6
# print("local_mac = ", hex(local_mac[0]), hex(local_mac[1]),
# hex(local_mac[2]), hex(local_mac[3]),
# hex(local_mac[4]), hex(local_mac[5]))
local_cc = get_device_company_code(scan_response[index:index + 4])
index = index + 4
# print("local_cc = ", local_cc)
local_ver = scan_response[index:index + 6]
index = index + 6
# print("local_ver = ", local_ver)
local_bat = get_device_battery_info(scan_response[index:index + 5])
index = index + 5
# print("local_bat = ", local_bat)
local_name = get_device_name_in_string_format(list(scan_response[index:index + 20]))
index = index + 20
# print("local_name = ", local_name)
# addr type is don't care in BMD380
local_addr_type = int(scan_response[index:index + 1][0])
# local_addr_type = 0xFF
index = index + 1
# print("local_addr_type = ", local_addr_type)
response = is_headstage_device_central_version(local_mac,
local_addr_type,
local_name,
local_cc,
local_ver,
local_bat)
if response is not None:
self.log_info('found', address_str(response.mac_address), response.serial_number)
self._interface.flush_input()
# apppend into db
devicesList = DeviceAPI.getByMac(address_str(response.mac_address))
if devicesList is not None:
if len(devicesList) == 0:
DeviceAPI.create(response.device_name, local_ver, address_str(response.mac_address))
found.append(response)
# print('scan_done_found', found)
self._found_with_id.append((response, 0 + 1))
callback(response)
return True
def decode_uart_preamble(self, raw_uart: tuple, expect_ret_len: int = 0) -> Optional[list]: def decode_uart_preamble(self, raw_uart: tuple, expect_ret_len: int = 0) -> Optional[list]:
# print("decode_uart_preamble: raw_uart = ", raw_uart) # print("decode_uart_preamble: raw_uart = ", raw_uart)
if raw_uart is None: if raw_uart is None:
@@ -1989,154 +2089,34 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
return self._found return self._found
def check_mem_survive(self) -> Optional[CC2650Device]: def check_mem_survive(self) -> Optional[CC2650Device]:
chk_mem_response = None ack = []
ins = bytearray() ins = bytearray()
ins.append(10)
ins.append(0x0A) ins.append(1) #length
ins.append(0x01) #length
ins.append(0xF1) ins.append(0xF1)
self._interface.flush_input() # print('ins', list(ins))
try: try:
self.log_verbose('[CC2650]', 'check_mem_survive att_write','0x'+str.upper(ins.hex()))
self._cc2650.send("bytes", bytes(ins)) self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException: except SerialTimeoutException as e:
self.log_verbose('[CC2650]', 'check_mem_survive send fail') raise RecvTimeout('device CC2650 check_mem_survive timeout') from e
else: else:
try: try:
chk_mem_response = self._cc2650.recv_uart(0.001) ack = self._cc2650.recv_uart(0.001)
except RecvTimeout: except RecvTimeout:
self.log_verbose('[CC2650]', 'check_mem_survive response timeout, no memory board') self.log_info("no memory board")
if chk_mem_response is None: # else:
return False # print('ack=', ack)
pack_len = chk_mem_response[0] if ack == [3]:
mem_ack = chk_mem_response[1:pack_len+1]
if mem_ack == [3]:
chk_mem_response_hex = ''.join(format(i, '02X') for i in chk_mem_response)
self.log_verbose('[CC2650]', 'check_mem_survive success', '0x'+chk_mem_response_hex)
return True return True
return False return False
@synchronized
def scan_callback(self, callback: Callable[[DeviceResponseInfo], None], timeout=5, all_device=False) -> bool:
self._found = found = []
self._found_with_id = []
scan_response = None
ins = bytearray()
ins.append(0x03)
ins.append(0x01) #length
ins.append(0xF1)
self._interface.flush_input()
for _ in range(5):
try:
self.log_verbose('[CC2650]', 'scan_callback att_write','0x'+str.upper(ins.hex()))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException:
self.log_verbose('[CC2650]', 'scan_callback send fail, rescan')
continue
else:
try:
scan_response = self._cc2650.recv_uart(0.01)
except RecvTimeout:
self.log_verbose('[CC2650]', 'scan_callback response timeout, no device, rescan')
continue
else:
if scan_response is None:
self.log_verbose('[CC2650]', 'scan_callback response is None, rescan')
continue
else:
break
if scan_response is None:
self.log_verbose('[CC2650]', 'scan_callback response is None--2')
return False
scan_response_hex = ''.join(format(i, '02X') for i in scan_response)
self.log_verbose('[CC2650]', 'scan_callback success', '0x'+scan_response_hex)
if len(scan_response) <= 2:
print('******************************************************************')
print('******************************************************************')
print('******************************************************************')
print('******************************************************************')
print('****need to fix, len(scan) <= 2')
print()
return False
pack_len = scan_response[0]
device_info_pkg = scan_response[1:pack_len+1]
# instruction format:
# ins[0]: get_scan_response = 0x04
# ins[1]: number of scanned device=0; a certain device = device_id (could be 1~8)
# ins[2]: addr=MAC=1, localName=2, company_code=3, version_info=4, battery_info=5, all_info=6;
# attr_length=0, e.g. len(addr)=6, len(company_code)=4
local_mac = None
local_cc = None
local_ver = None
local_bat = None
local_name = None
local_addr_type = None
# get device attribute length
attr_length = [6, 4, 6, 5, 20]
index = 0
local_mac = get_device_mac_in_address_format(device_info_pkg[index:index + 6])
index = index + 6
local_cc = get_device_company_code(device_info_pkg[index:index + 4])
index = index + 4
local_ver = device_info_pkg[index:index + 6]
index = index + 6
local_bat = get_device_battery_info(device_info_pkg[index:index + 5])
index = index + 5
local_name = get_device_name_in_string_format(list(device_info_pkg[index:index + 20]))
index = index + 20
local_addr_type = int(device_info_pkg[index:index + 1][0])
index = index + 1
response = is_headstage_device_central_version(local_mac,
local_addr_type,
local_name,
local_cc,
local_ver,
local_bat)
if response is not None:
self.log_info('found', address_str(response.mac_address), response.serial_number)
self._interface.flush_input()
# apppend into db
devicesList = DeviceAPI.getByMac(address_str(response.mac_address))
if devicesList is not None:
if len(devicesList) == 0:
DeviceAPI.create(response.device_name, local_ver, address_str(response.mac_address))
found.append(response)
# print('scan_done_found', found)
self._found_with_id.append((response, 0 + 1))
callback(response)
return True
@synchronized @synchronized
def connect(self, response: DeviceResponseInfo, direct_connect: bool = False) -> Optional[CC2650Device]: def connect(self, response: DeviceResponseInfo, direct_connect: bool = False) -> Optional[CC2650Device]:
if self._handle is not None: if self._handle is not None:
@@ -2146,199 +2126,225 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
addr_type = response.addr_type addr_type = response.addr_type
address_s = cc2650.address_str(address) address_s = cc2650.address_str(address)
self.log_info(DEVICE_CONNECTING, address_s)
connect_ins = bytearray()
connect_ins.append(5)
connect_ins.append(8) #length
connect_ins.append(addr_type)
connect_ins.append(address[0])
connect_ins.append(address[1])
connect_ins.append(address[2])
connect_ins.append(address[3])
connect_ins.append(address[4])
connect_ins.append(address[5])
connect_ins.append(0xF1)
connected = False connected = False
connect_response = None
ins = bytearray()
ins.append(0x05) # send connect command
ins.append(0x08) #length if direct_connect is True:
ins.append(addr_type)
ins.append(address[0])
ins.append(address[1])
ins.append(address[2])
ins.append(address[3])
ins.append(address[4])
ins.append(address[5])
ins.append(0xF1)
self._interface.flush_input()
for retry in range(5): # send device mac and addrType
try: try:
self.log_verbose('[CC2650]', 'connect att_write', '0x'+str.upper(ins.hex())) self._cc2650.send("bytes", bytes(connect_ins))
self._cc2650.send("bytes", bytes(ins))
except RecvTimeout: except SerialTimeoutException as e:
self.log_verbose('[CC2650]', 'connect send fail') raise RecvTimeout('device CC2650 connect fail') from e
continue return None
else: # connection establish done?
for retry_recv_ack in range(10):
try: try:
connect_response = self._cc2650.recv_uart(2) con_done = self._cc2650.recv_uart(timeout = 0.5)
# print("con_done = ", con_done)
except RecvTimeout: except RecvTimeout:
# self.log_verbose('[CC2650]', 'connect response timeout') self.log_info("recv connection timeout, retry... ")
if retry < 5: continue
self.log_verbose('[CC2650]', 'connect retry')
continue # is the ack valid?
else: if con_done is None:
continue
elif con_done[0] is 46 and \
con_done[1] is 80 and \
con_done[2] is 48 and \
con_done[3] is 4:
connected = True
break break
else:
for dev in self._found:
if dev.mac_address == address:
if connect_response is None: # send device mac and addrType
self.log_verbose('[CC2650]', 'connect response timeout') try:
return False # print('send_connect',bytes(connect_ins))
self._cc2650.send("bytes", bytes(connect_ins))
pack_len = connect_response[0] except SerialTimeoutException as e:
connect_ack = connect_response[1:pack_len+1] raise RecvTimeout('device CC2650 connect fail') from e
if pack_len == 1:
if connect_ack[0] == 3:
connected = True
connect_response_hex = ''.join(format(i, '02X') for i in connect_response)
self.log_verbose('[CC2650]', 'connect success', '0x'+connect_response_hex)
elif pack_len == 4: else:
if connect_ack[0] == 46 and connect_ack[1] == 80 and \ sleep(2)
connect_ack[2] == 48 and connect_ack[3] == 4:
connected = True
connect_response_hex = ''.join(format(i, '02X') for i in connect_response)
self.log_verbose('[CC2650]', 'connect success', '0x'+connect_response_hex)
if connected == True: # connection establish done?
# CC2650Device(device_id, master, scan_response) is a slave device for retry_recv_ack in range(5):
# device_id is don't care, because it will be overwrite later self._cc2650.send("bytes", bytes((0, 0, 0, 0)))
dont_care = 0
self._device = ret = CC2650Device(device_id=dont_care, master=self, response_info=response)
self.log_verbose('[CC2650]', DEVICE_CONNECTED, address_s)
return ret
connect_response_hex = ''.join(format(i, '02X') for i in connect_response) try:
self.log_verbose('[CC2650]', 'connect fail', '0x'+connect_response_hex) con_done = self._cc2650.recv_uart(timeout = 0.1)
return False
except RecvTimeout:
self.log_info("recv connection timeout, retry... ")
continue
# is the ack valid?
if con_done is None:
continue
elif con_done[0] is 46 and \
con_done[1] is 80 and \
con_done[2] is 48 and \
con_done[3] is 4:
connected = True
# print('con_done=', con_done)
break
else:
continue
# if select device is invalid or connect failed
self._interface.flush_input()
if connected is False:
if direct_connect is True:
self.reset_internal()
self.reset_hardware()
self._interface.flush()
return None
# CC2650Device(device_id, master, scan_response) is a slave device
# device_id is don't care, because it will be overwrite later
dont_care = 0
self._device = ret = CC2650Device(device_id=dont_care, master=self, response_info=response)
self.log_info(DEVICE_CONNECTED, address_s)
sleep(0.5)
print('ret',ret)
return ret
@synchronized @synchronized
def disconnect(self, device: int, force=False) -> bool: def disconnect(self, device: int, force=False) -> bool:
self.log_info(DEVICE_DISCONNECTING, device)
ins = bytearray() ins = bytearray()
ins.append(8)
ins.append(0x08) ins.append(1) #length
ins.append(0x01) #length
ins.append(0xF1) ins.append(0xF1)
self._interface.flush_input()
for retry in range(5):
try:
self.log_verbose('[CC2650]', 'disconnect att_write','0x'+str.upper(ins.hex()))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException:
self.log_verbose('[CC2650]', 'disconnect send fail')
continue
else:
self.reset_internal()
self.reset_hardware()
self.log_verbose('[CC2650]', 'disconnect success')
return True
# try:
# disconnect_response = self._cc2650.recv_uart(0.01)
# print(disconnect_response)
# except RecvTimeout:
# self.log_verbose('[CC2650]', 'disconnect response timeout')
# if retry < 5:
# self.log_verbose('[CC2650]', 'connect retry')
# continue
# else:
# break
# if disconnect_response is None:
# return False
# pack_len = disconnect_response[0]
# disconnect_ack = disconnect_response[1:pack_len+1]
# if disconnect_ack == [3]:
# disconnect_response_hex = ''.join(format(i, '02X') for i in disconnect_response)
# self.log_verbose('[CC2650]', 'disconnect success', '0x'+disconnect_response_hex)
# self.reset_internal()
# self.reset_hardware()
# return True
self.log_verbose('[CC2650]', 'disconnect fail')
return False
@synchronized
def write_characteristic(self, device: int, handle: int, data: bytes) -> bool:
write_response = None
ins = bytearray()
ins.append(0x06)
ins.append(len(data)+2) #length = handle + C0C0XXXX(data len) + F1
ins.append(handle)
ins.extend(data)
ins.append(0xF1)
self._interface.flush_input()
try: try:
self.log_verbose('[CC2650]', 'write_characteristic', device, str(hex(handle).upper())) # print('send_disconnect',bytes(ins))
self.log_verbose('[CC2650]', 'write_characteristic att_write','0x'+str.upper(ins.hex()))
self._cc2650.send("bytes", bytes(ins)) self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException: except RecvTimeout:
self.log_verbose('[CC2650]', 'write_characteristic send fail') self.log_warn('disconnect time out')
return False
except RuntimeError as e:
self.log_warn('suppressed error', str(e))
return False
else: else:
try: sleep(0.01)
write_response = self._cc2650.recv_uart(0.5)
except RecvTimeout:
self.log_verbose('[CC2650]', 'write_characteristic response timeout')
if write_response is None:
self.log_verbose('[CC2650]', 'write_characteristic fail')
return False
pack_len = write_response[0]
write_ack = write_response[1:pack_len+1]
if write_ack == [3]:
write_response_hex = ''.join(format(i, '02X') for i in write_response)
self.log_verbose('[CC2650]', 'write_characteristic success', '0x'+write_response_hex)
return True return True
self.log_verbose('[CC2650]', 'write_characteristic fail') finally:
return False self.log_info(DEVICE_DISCONNECTED, device)
# reset single 2650 after disconnected
self.reset_internal()
self.reset_hardware()
self._interface.flush()
@synchronized @synchronized
def read_characteristic(self, device: int, handle: int) -> Optional[bytes]: def read_characteristic(self, device: int, handle: int) -> Optional[bytes]:
read_response = None
ins = bytearray()
ins.append(0x07) # print("read_characteristic, expect_data_length = ", expect_data_length)
ins.append(0x02) #length
ret = None
ins = bytearray()
ins.append(7)
ins.append(2) #length
ins.append(handle) ins.append(handle)
ins.append(0xF1) ins.append(0xF1)
self._interface.flush_input()
for _ in range(2):
try:
# print('send_read',bytes(ins))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException:
raise RecvTimeout('device CC2650 send read_characteristic fail')
try:
ret = self._cc2650.recv_uart(timeout = 2)
except Exception as e2:
raise RecvTimeout()
else:
# print("======== read ret = ", ret)
self._interface.flush_input()
# try:
# ret = self._cc2650.recv_uart(timeout = 1)
# except Exception as e2:
# raise RecvTimeout()
# else:
# # print("read_characteristic ret = ", ret)
# pass
if ret is None:
return None
return bytes(ret)
@synchronized
def write_characteristic(self, device: int, handle: int, data: bytes):
# print("device", device)
# print("handle", handle)
# print("data", data.hex())
# self.log_info('reset')
self.log_verbose('write_characteristic', device, handle)
self.log_verbose('[CC2650]', 'att_write', str.upper(data.hex()))
ack = None
ret = None
data_array = bytearray()
data_array.append(6)
data_array.append(len(data)+2) #length
data_array.append(handle)
data_array.extend(data)
data_array.append(0xF1)
try: try:
self.log_verbose('[CC2650]', 'read_characteristic', device, str(hex(handle).upper())) self._cc2650.send("bytes", bytes(data_array))
self.log_verbose('[CC2650]', 'read_characteristic att_write','0x'+str.upper(ins.hex())) # print('send_write',bytes(data_array))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException: except SerialTimeoutException as e:
self.log_verbose('[CC2650]', 'read_characteristic send fail') raise RecvTimeout('device CC2650 send instruction fail') from e
# read error code
try:
ret = self._cc2650.recv_uart()
# print("_______ write ack = ", ret)
except Exception as e2:
pass
else: else:
try: pass
read_response = self._cc2650.recv_uart(1)
except RecvTimeout: self._interface.flush_input()
self.log_verbose('[CC2650]', 'read_characteristic response timeout') self._interface.flush_output()
if read_response is None:
return None
read_response_hex = ''.join(format(i, '02X') for i in read_response)
self.log_verbose('[CC2650]', 'read_characteristic success', '0x'+read_response_hex)
return bytes(read_response)
def set_notify(self, device: Union[int, Device], enable: bool): def set_notify(self, device: Union[int, Device], enable: bool):
if isinstance(device, CompletedDevice): if isinstance(device, CompletedDevice):
@@ -2439,6 +2445,7 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
self._cc2650_log_level = self.log_level self._cc2650_log_level = self.log_level
def read_characteristic(self, device: int, handle: int) -> Optional[bytes]: def read_characteristic(self, device: int, handle: int) -> Optional[bytes]:
self.log_verbose('read_characteristic', device, '0x%02X' % handle)
master = self._cc2650[device] master = self._cc2650[device]
@@ -2496,6 +2503,11 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
self._interface.flush() self._interface.flush()
self._selector.select(device_id) self._selector.select(device_id)
# self._mem_selector.select(device_id)
# print("multiMaster selector = ", device_id)
# print("\n")
# device._notify_flag = enable
try: try:
master.write_characteristic(device_id, self.NOTIFY_HANDLE, value) master.write_characteristic(device_id, self.NOTIFY_HANDLE, value)
except SendInstructionTimeoutError: except SendInstructionTimeoutError:
+19 -31
View File
@@ -253,7 +253,6 @@ class DeviceInternalCommandHandler:
return True return True
# why inherit MasterDevice ?
class DeviceManager(MasterDevice, Synchronized): class DeviceManager(MasterDevice, Synchronized):
"""device manager. It manager the device libraries, found device, connected device """device manager. It manager the device libraries, found device, connected device
and the control of the master device. and the control of the master device.
@@ -268,6 +267,7 @@ class DeviceManager(MasterDevice, Synchronized):
options: DeviceManagerOptions, options: DeviceManagerOptions,
handler: Optional[DeviceInternalCommandHandler] = None): handler: Optional[DeviceInternalCommandHandler] = None):
""" """
:param options: manager options. :param options: manager options.
:param handler: device internal command handler :param handler: device internal command handler
""" """
@@ -277,7 +277,6 @@ class DeviceManager(MasterDevice, Synchronized):
if handler is None: if handler is None:
handler = DeviceInternalCommandHandler() handler = DeviceInternalCommandHandler()
# control server api
self._handler: DeviceInternalCommandHandler = handler self._handler: DeviceInternalCommandHandler = handler
self._options: DeviceManagerOptions = options self._options: DeviceManagerOptions = options
@@ -287,9 +286,7 @@ class DeviceManager(MasterDevice, Synchronized):
# hardware # hardware
self._interface = options.interface self._interface = options.interface
self._master: Optional[MasterDevice] = None self._master: Optional[MasterDevice] = None
# memory board
self._centralMaster: Optional[MasterDevice] = None self._centralMaster: Optional[MasterDevice] = None
# connected device
self._device: List[CompletedDevice] = [] self._device: List[CompletedDevice] = []
# demo device # demo device
@@ -339,10 +336,6 @@ class DeviceManager(MasterDevice, Synchronized):
def library_path(self) -> List[Path]: def library_path(self) -> List[Path]:
return self._repository.library_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 @logging_info
def reload_library(self): def reload_library(self):
"""reload device library""" """reload device library"""
@@ -574,23 +567,20 @@ class DeviceManager(MasterDevice, Synchronized):
:param device: device ID or response info :param device: device ID or response info
:return: CompletedDevice, None if not found. :return: CompletedDevice, None if not found.
""" """
try: if isinstance(device, int):
if isinstance(device, int): for slave in self._device:
for slave in self._device: if slave.device_id == device:
if slave.device_id == device: return slave
return slave
elif isinstance(device, str): elif isinstance(device, str):
for slave in self._device: for slave in self._device:
if ':'.join('{:02x}'.format(b) for b in slave.mac_address) == device.lower(): if ':'.join('{:02x}'.format(b) for b in slave.mac_address) == device:
return slave return slave
elif isinstance(device, DeviceInfo): elif isinstance(device, DeviceInfo):
for slave in self._device: for slave in self._device:
if device.match(slave): if device.match(slave):
return slave return slave
except BaseException as e:
self.log_error('device not found', str(device))
return None return None
@@ -832,9 +822,6 @@ class DeviceManager(MasterDevice, Synchronized):
elif func == InternalInstruction.PREDEFINED_DISABLE_CACHE: elif func == InternalInstruction.PREDEFINED_DISABLE_CACHE:
self._device_disable_cache(device, *para) self._device_disable_cache(device, *para)
elif func == InternalInstruction.PREDEFINED_IDLE:
self._idle(device, *para)
elif isinstance(device, DebugDevice): elif isinstance(device, DebugDevice):
if func == InternalInstruction.PREDEFINED_NOTIFY: if func == InternalInstruction.PREDEFINED_NOTIFY:
@@ -854,11 +841,6 @@ class DeviceManager(MasterDevice, Synchronized):
InternalInstruction.PREDEFINED_DATA_FORMAT, InternalInstruction.PREDEFINED_DATA_FORMAT,
f) f)
def _idle(self, device: Device, expr: AnyStr):
self._handler.device_internal_command(device.device_id,
InternalInstruction.PREDEFINED_IDLE,
None)
def _device_data_format_cali(self, device: Device, expr: str, cali: bytes = None): def _device_data_format_cali(self, device: Device, expr: str, cali: bytes = None):
if cali is None: if cali is None:
cali = device.calibration_info(expr) cali = device.calibration_info(expr)
@@ -1041,9 +1023,15 @@ class DeviceManager(MasterDevice, Synchronized):
raise RuntimeError(DEVICE_NOT_FOUND, device) raise RuntimeError(DEVICE_NOT_FOUND, device)
if DeviceCommonInstruction(instruction) is not None: if DeviceCommonInstruction(instruction) is not None:
# print('deviceCommonInstr')
# print(instruction)
device.call_instruction(instruction) device.call_instruction(instruction)
elif instruction in device.library.instruction_table: elif instruction in device.library.instruction_table:
# print('instruction')
# print(instruction)
device.call_instruction(instruction) device.call_instruction(instruction)
else: else:
raise RuntimeError(INSTRUCTION_UNKNOWN, instruction) raise RuntimeError(INSTRUCTION_UNKNOWN, instruction)
+205 -409
View File
@@ -1,7 +1,6 @@
import abc import abc
import struct import struct
import math import math
import numpy
from typing import Optional, TypeVar, Generic, Tuple, Dict, List, AnyStr from typing import Optional, TypeVar, Generic, Tuple, Dict, List, AnyStr
from datetime import datetime from datetime import datetime
@@ -854,8 +853,7 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
__slots__ = ('_message', '_cycle_number', '_start_return_data', '_time_stamp', __slots__ = ('_message', '_cycle_number', '_start_return_data', '_time_stamp',
'_total_time_stamp', '_mode', '_cycle_start_time', '_total_time_stamp', '_mode', '_cycle_start_time',
'_mode_stop', '_show_data', '_mode_stop')
'_last_mem_wrong_information', '_last_mem_cnt', '_last_elite_notify_times')
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -870,12 +868,6 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
self._mode = 0 self._mode = 0
self._cycle_start_time = [] self._cycle_start_time = []
self._show_data = False
self._last_mem_wrong_information = -1
self._last_mem_cnt = -1
self._last_elite_notify_times = -1
@property @property
def name(self) -> str: def name(self) -> str:
return self.NAME return self.NAME
@@ -889,71 +881,22 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
def decode(self, data: bytes) -> Optional[RecordingData]: def decode(self, data: bytes) -> Optional[RecordingData]:
if len(data) < 18: if len(data) < 18:
return None return None
#/* Elite Notify data:
# * +--------+----------+---------+---------+---------+-----------+-----------------+
# * | id(1B) | time(4B) | ch1(4B) | ch2(4B) | ch3(4B) | cycle(2B) | finish_flag(1B) |
# * | bat(4B) | notify#(1B) | ch4(4B) | ch5(4B) | ch6(4B) | __(3B) |
# * +---------+-------------+---------+---------+---------+--------+
# */
#/*
# * EliteADCControl(): use ADC plot, and send what data to controller
# * +---------------------------+-----------+-----------+-----------+-----------+-----------+
# * | MODE | ch1 | ch2 | ch3 | cycle | ch4 |
# * +---------------------------+-----------+-----------+-----------+-----------+-----------+
# * | CURVE_IV | Iin | Vout | Vin | | Vmon |
# * | CURVE_IV_CY | Iin | Vout | Vin | v | Vmon |
# * | CURVE_VO | Iin | Vout | Vin | | Vmon |
# * | CURVE_RT | Iin | Vout | R | | Vmon |
# * | CURVE_VT | Iin | Vin | | | |
# * | CURVE_IT | Iin | Vin | Vout | | Vmon |
# * | CURVE_CC | Iin | Vin | Vout | | Vmon |
# * | CURVE_CP | Iin | Vout-Vin | Vout | | Vmon |
# * | CURVE_CV | Iin | Vout-Vin | Vout | v | Vmon |
# * | CURVE_LSV | Iin | Vout-Vin | Vout | | Vmon |
# * | CURVE_CA | Iin | Vout-Vin | Vout | | Vmon |
# * | CURVE_OCP | Iin | Vmon-Vin | Vin | | Vmon |
# * | CURVE_UNI_PULSE | pul1_Iin | pul2_Iin | | | |
# * | CURVE_DPV | c1&c2_avg | Vout-Vin | Vout | | Vmon |
# * | CURVE_DPV_SMPRATE | Iin | Vout-Vin | Vout | | Vmon |
# * | CURVE_DPV_ADVANCE | c1&c2_avg | Vout-Vin | Vout | | Vmon |
# * | CURVE_DPV_ADVANCE_SMPRATE | Iin | Vout-Vin | Vout | | Vmon |
# * +---------------------------+-----------+-----------+-----------+-----------+-----------+
# *
# * ps. c1_avg = pul1_Iin
# * ps. c2_avg = pul2_Iin
# */
mem_cnt = data[1] current = struct.unpack('>i', data[1:5])[0] # unit: 1/1000 nA
time_stamp: float = struct.unpack('<I', data[1+3:5+3])[0] voltage = struct.unpack('>i', data[5:9])[0] # unit: mV
ch1 = struct.unpack('<i', data[5+3:9+3])[0] # unit: nA impedance = struct.unpack('>i', data[9:13])[0] # unit: kOm
ch2 = struct.unpack('<i', data[9+3:13+3])[0] # unit: uV time_stamp: float = struct.unpack('<I', data[13:17])[0] # unit: ms
ch3 = struct.unpack('<i', data[13+3:17+3])[0] # unit: mOm cycle_number = struct.unpack('>H', data[17:19])[0]
finish_mode_falg = data[19]
cycle_number = struct.unpack('<H', data[17+3:19+3])[0] mem_wrong_information = struct.unpack('>i', data[40:40+4])[0]
finish_mode_falg = data[19+3]
battery = struct.unpack('<i', data[20+3:24+3])[0]
elite_notify_times = data[24+3]
ch4 = struct.unpack('<i', data[25+3:29+3])[0]
ch5 = struct.unpack('<i', data[29+3:33+3])[0]
ch6 = struct.unpack('<i', data[33+3:37+3])[0]
# self._show_data = True
mem_wrong_information = struct.unpack('<i', data[43:47])[0] # mem_wrong_information = green retry, green wrong, red retry, red wrong # print('decode', list(data[20:]))
mem_wrong = data[40]
if mem_wrong_information != self._last_mem_wrong_information: mem_retry_cnt = data[41]
print(datetime.now(), 'device', str(self.device), 'mem_wrong_information[43:47]:', data[43:47], mem_wrong_information, self._last_mem_wrong_information, flush = True) mem_green_wrong = data[42]
if mem_cnt != self._last_mem_cnt+1: mem_green_retry_cnt = data[43]
if not (mem_cnt == 0 and self._last_mem_cnt == 255): ram_num = data[44]
print(datetime.now(), 'device', str(self.device), 'mem_cnt:', mem_cnt, 'self._last_mem_cnt:', self._last_mem_cnt, flush = True)
if (elite_notify_times != self._last_elite_notify_times+1) and not (elite_notify_times == 0 and self._last_elite_notify_times == 0):
if not (elite_notify_times == 0 and self._last_elite_notify_times == 255):
print(datetime.now(), 'device', str(self.device), 'elite_notify_times:', elite_notify_times, 'self._elite_notify_times:', self._last_elite_notify_times, flush = True)
self._last_mem_wrong_information = mem_wrong_information
self._last_mem_cnt = mem_cnt
self._last_elite_notify_times = elite_notify_times
ram_num = data[47]
broken_flag = data[-1] broken_flag = data[-1]
if (finish_mode_falg & 0b11110000 == 0b10100000): if (finish_mode_falg & 0b11110000 == 0b10100000):
@@ -974,31 +917,26 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
print("error timeStamp full data:", list(data), datetime.now(), '\n') print("error timeStamp full data:", list(data), datetime.now(), '\n')
return None return None
else: else:
if self._show_data: # print('|', time_stamp, '|', delta, '|', current, '|', voltage, '|', impedance,
print('|', time_stamp, '|', delta, '|', int(time_stamp * 1000 / 2), # '|', cycle_number, '|', finishMode, '@', str(self.device))
'|', ch1, '|', ch2, '|', ch3, '|', cycle_number, # print('|', '{:10}'.format(time_stamp),
'|', ch4, '|', ch5, '|', ch6, # '|', '{:10}'.format(delta),
'|', finishMode, '@', str(self.device), flush = True) # '|', '{:10}'.format(current),
# '|', '{:10}'.format(voltage),
# '|', '{:10}'.format(impedance),
# '|', '{:5}'.format(cycle_number),
# '|', '{:1}'.format(finishMode),
# '@', str(self.device), '|')
# print('|', '{:10}'.format(time_stamp), # print('|', '{:5}'.format(mem_wrong),
# '|', '{:4}'.format(delta), # '|', '{:5}'.format(mem_retry_cnt),
# '|', '{:10}'.format(int(time_stamp * 1000 / 2)), #[usec] # '|', '{:5}'.format(mem_green_wrong),
# '|', '{:10}'.format(ch1), #[nA] # '|', '{:5}'.format(mem_green_retry_cnt),
# '|', '{:10}'.format(ch2), #[uV] # '|', '{:5}'.format(mem_wrong_information),
# '|', '{:10}'.format(ch3), # '|', '{:5}'.format(ram_num),
# '|', '{:5}'.format(cycle_number), # '|', '{:5}'.format(broken_flag),
# '|', '{:10}'.format(ch4), #Voutin[uV] # '@', str(self.device), '|')
# '|', '{:10}'.format(ch5), pass
# '|', '{:10}'.format(ch6),
# '|', '{:5}'.format(battery), #[mV]
# '|', '{:4}'.format(elite_notify_times),
# '|', '{:1}'.format(finishMode),
# '@', str(self.device), '|', flush = True)
# print('|', '{:5}'.format(mem_wrong_information),
# '|', '{:2}'.format(ram_num),
# '|', '{:2}'.format(broken_flag),
# '@', str(self.device), '|')
if finishMode == True: if finishMode == True:
print("finishMode full data:", list(data), datetime.now()) print("finishMode full data:", list(data), datetime.now())
@@ -1007,27 +945,20 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
self._mode_stop = 0 self._mode_stop = 0
ret = RecordingData(self.device, int(time_stamp * 1000 / 2), 0) ret = RecordingData(self.device, int(time_stamp * 1000 / 2), 0)
ret.append_data(0, ch1) ret.append_data(0, current)
ret.append_data(1, ch2) ret.append_data(1, voltage)
ret.append_data(2, ch3) ret.append_data(2, impedance)
ret.append_data(3, cycle_number) ret.append_data(3, cycle_number)
ret.append_data(4, ch4)
ret.append_data(5, ch5)
ret.append_data(6, ch6)
# ret.append_data(4, battery)
# ret.append_data(5, elite_notify_times)
# ret.append_data(6, mem_cnt)
# # memoryboard information
# ret.append_data(7, ram_num)
# ret.append_data(8, broken_flag)
# try:
# ret.append_data(9, mem_wrong_information)
# # print('append_data success, mem_wrong_information:', mem_wrong_information, hex(mem_wrong_information))
# except:
# print('append_data fail, mem_wrong_information:', mem_wrong_information, hex(mem_wrong_information))
# memoryboard information
# ret.append_data(4, mem_wrong)
# ret.append_data(5, mem_retry_cnt)
# ret.append_data(6, mem_green_wrong)
# ret.append_data(7, mem_green_retry_cnt)
ret.append_data(4, ram_num)
ret.append_data(5, broken_flag)
ret.append_data(6, mem_wrong_information)
# ret.append_data(4, ram_num)
if cycle_number != self._cycle_number: if cycle_number != self._cycle_number:
# notify cycle_number change # notify cycle_number change
@@ -1401,9 +1332,9 @@ class EISZeroOneDataDecoder(RecDataDecoder):
__slots__ = ('_message', '_cycle_number', '_start_return_data', '_time_stamp', __slots__ = ('_message', '_cycle_number', '_start_return_data', '_time_stamp',
'_total_time_stamp', '_mode', '_cycle_start_time', '_total_time_stamp', '_mode', '_cycle_start_time',
'_mode_stop', '_last_time_stamp', '_last_delta', '_mode_stop', '_last_time_stamp', '_last_delta', '_cali_coeff',
'_cali_package', 'cali_coeff', '_ac_amp', '_mode', 'cali_coeff', '_ac_amp', '_mode', '_freq_start', '_freq_stop',
'_last_phase', '_first_phase_flag', '_show_data') '_freq_direction', '_last_phase', '_first_phase_flag')
def __init__(self, cali_coeff: bytes = None): def __init__(self, cali_coeff: bytes = None):
super().__init__() super().__init__()
@@ -1416,234 +1347,92 @@ class EISZeroOneDataDecoder(RecDataDecoder):
self._mode_stop = 0 self._mode_stop = 0
self._cycle_start_time = [] self._cycle_start_time = []
self._ac_amp: int = 0
self._mode: int = 0 self._mode: int = 0
self._freq_start: int = 0
self._freq_stop: int = 0
self._freq_direction = 0
self._last_phase = 0 self._last_phase = 0
self._first_phase_flag = 1 self._first_phase_flag = 1
self._cali_package: Optional[bytes] = None self._cali_coeff: Optional[bytes] = None
self.cali_coeff: Optional[List[Tuple[int, int]]] = None self.cali_coeff: Optional[List[Tuple[int, int]]] = None
self._show_data = False if cali_coeff is not None:
self._cali_coeff = cali_coeff
if self._cali_package is None: self.cali_coeff = self._decode_cali_coeff(cali_coeff)
self._cali_package = cali_coeff
self.cali_coeff = self._decode_cali_coeff(self._cali_package)
@staticmethod @staticmethod
def _decode_cali_coeff(cali_coeff: bytes) -> Optional[List[Tuple[int, int]]]: def _decode_cali_coeff(cali_coeff: bytes) -> Optional[List[Tuple[int, int]]]:
if cali_coeff != b'': if cali_coeff != b'':
cis_data_len = 20
cali_table = [] cali_table = []
phase_para_a = []
phase_para_b = []
hsrtia_a = [] hsrtia_a = []
hsrtia_b = [] hsrtia_b = []
rolloff = [] hsrtia_c = []
phase_coeff = [] hsrtia_d = []
phase_offset = []
phase_coeff = numpy.zeros([8, 4], dtype = int) # print('cali_coeff', cali_coeff)
phase_offset = numpy.zeros([8, 4], dtype = int) cutoff_freq = struct.unpack('>I', cali_coeff[1:5])[0] * 100 #4
# temp = struct.unpack('>B', cali_coeff[5:6])[0] #1
# hsrtia_200r = struct.unpack('>B', cali_coeff[6:7])[0] #1
# hsrtia_5k = struct.unpack('>H', cali_coeff[7:9])[0] #2
# hsrtia_20k = struct.unpack('>H', cali_coeff[6:8])[0] #2
# hsrtia_160k = struct.unpack('>I', cali_coeff[8:12])[0] #4
######################################## index = 20
# phase_coeff for i in range(index, index+16, 8):
# [[freq0, freq1, freq2, freq3] ----->gain0 phase_para_a.append(struct.unpack('>i', cali_coeff[i+1:i+5])[0])
# [freq0, freq1, freq2, freq3] ----->gain1 phase_para_b.append(struct.unpack('>i', cali_coeff[i+5:i+9])[0])
# [freq0, freq1, freq2, freq3] ----->gain2
# [freq0, freq1, freq2, freq3] ----->gain3
# ]
#######################################
#hstia=0 index = 40
cis_cali_packet = 1 for i in range(index, index+16, 8):
index = (cis_cali_packet - 1) * cis_data_len phase_para_a.append(struct.unpack('>i', cali_coeff[i+1:i+5])[0])
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0]) phase_para_b.append(struct.unpack('>i', cali_coeff[i+5:i+9])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 2 #Lv[0] 160k
index = (cis_cali_packet - 1) * cis_data_len index = 60
g = 0 hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0] hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0] hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0] hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 3
index = (cis_cali_packet - 1) * cis_data_len
g = 0
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=1
cis_cali_packet = 4
index = (cis_cali_packet - 1) * cis_data_len
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 5 #Lv[1] 20k
index = (cis_cali_packet - 1) * cis_data_len index = 80
g = 1 hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0] hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0] hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0] hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 6
index = (cis_cali_packet - 1) * cis_data_len
g = 1
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=2 #Lv[2] 5k
cis_cali_packet = 7 index = 100
index = (cis_cali_packet - 1) * cis_data_len hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0]) hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0]) hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0]) hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
cis_cali_packet = 8 #Lv[3] 200R
index = (cis_cali_packet - 1) * cis_data_len index = 120
g = 2 hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0] hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0] hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0] hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 9
index = (cis_cali_packet - 1) * cis_data_len
g = 2
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=3 # hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0])
cis_cali_packet = 10 # hsrtia_b.append(struct.unpack('>I', cali_coeff[index+5:index+9])[0]/1e6)
index = (cis_cali_packet - 1) * cis_data_len # hsrtia_c.append(struct.unpack('>I', cali_coeff[index+9:index+13])[0]/1e5)
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0]) # hsrtia_d.append(struct.unpack('>I', cali_coeff[index+13:index+17])[0]/1e6)
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 11 # print('cutoff_freq', cutoff_freq)
index = (cis_cali_packet - 1) * cis_data_len # print('hsrtia_a', hsrtia_a)
g = 3 # print('hsrtia_b', hsrtia_b)
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0] # print('hsrtia_c', hsrtia_c)
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0] # print('hsrtia_d', hsrtia_d)
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0] # print('phase_para_a', phase_para_a)
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0] # print('phase_para_b', phase_para_b)
cis_cali_packet = 12
index = (cis_cali_packet - 1) * cis_data_len
g = 3
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=4 cali_table.append((cutoff_freq, phase_para_a, phase_para_b, hsrtia_a, hsrtia_b, hsrtia_c, hsrtia_d))
cis_cali_packet = 13
index = (cis_cali_packet - 1) * cis_data_len
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 14
index = (cis_cali_packet - 1) * cis_data_len
g = 4
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 15
index = (cis_cali_packet - 1) * cis_data_len
g = 4
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=5
cis_cali_packet = 16
index = (cis_cali_packet - 1) * cis_data_len
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 17
index = (cis_cali_packet - 1) * cis_data_len
g = 5
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 18
index = (cis_cali_packet - 1) * cis_data_len
g = 5
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=6
cis_cali_packet = 19
index = (cis_cali_packet - 1) * cis_data_len
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 20
index = (cis_cali_packet - 1) * cis_data_len
g = 6
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 21
index = (cis_cali_packet - 1) * cis_data_len
g = 6
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
#hstia=7
cis_cali_packet = 22
index = (cis_cali_packet - 1) * cis_data_len
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+2:index+6])[0])
hsrtia_b.append(struct.unpack('>q', cali_coeff[index+6:index+14])[0])
rolloff.append(struct.unpack('>i', cali_coeff[index+14:index+18])[0])
cis_cali_packet = 23
index = (cis_cali_packet - 1) * cis_data_len
g = 7
phase_coeff[g][0] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][0] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][1] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][1] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
cis_cali_packet = 24
index = (cis_cali_packet - 1) * cis_data_len
g = 7
phase_coeff[g][2] = struct.unpack('>i', cali_coeff[index+2:index+6])[0]
phase_offset[g][2] = struct.unpack('>i', cali_coeff[index+6:index+10])[0]
phase_coeff[g][3] = struct.unpack('>i', cali_coeff[index+10:index+14])[0]
phase_offset[g][3] = struct.unpack('>i', cali_coeff[index+14:index+18])[0]
print('hsrtia_a', hsrtia_a)
print('hsrtia_b', hsrtia_b)
print('rolloff', rolloff)
print('phase_coeff')
print(phase_coeff)
print('phase_offset')
print(phase_offset)
cali_table.append((phase_coeff, phase_offset, hsrtia_a, hsrtia_b, rolloff))
return cali_table return cali_table
else: else:
@@ -1652,10 +1441,10 @@ class EISZeroOneDataDecoder(RecDataDecoder):
@property @property
def name(self) -> AnyStr: def name(self) -> AnyStr:
if self._cali_package is None: if self._cali_coeff is None:
return self.NAME return self.NAME
else: else:
return self.NAME.encode() + b':' + self._cali_package return self.NAME.encode() + b':' + self._cali_coeff
def message(self) -> Optional[str]: def message(self) -> Optional[str]:
ret = self._message ret = self._message
@@ -1667,20 +1456,15 @@ class EISZeroOneDataDecoder(RecDataDecoder):
if len(data) < 18: if len(data) < 18:
return None return None
ch1 = struct.unpack('>i', data[1+3:5+3])[0] # unit: 1/1000 nA ch1 = struct.unpack('>i', data[1:5])[0] # unit: 1/1000 nA
ch2 = struct.unpack('>i', data[5+3:9+3])[0] # unit: mV ch2 = struct.unpack('>i', data[5:9])[0] # unit: mV
ch3 = struct.unpack('>i', data[9+3:13+3])[0] # unit: kOm ch3 = struct.unpack('>i', data[9:13])[0] # unit: kOm
time_stamp: float = struct.unpack('<I', data[13+3:17+3])[0] # unit: ms time_stamp: float = struct.unpack('<I', data[13:17])[0] # unit: ms
cycle_number = struct.unpack('>H', data[17+3:19+3])[0] cycle_number = struct.unpack('>H', data[17:19])[0]
d19 = data[19+3] d19 = data[19]
gain = data[20+3] gain = (d19 & 0x0F)
finishMode = (d19 & 0x80) >> 7 finishMode = (d19 & 0x80) >> 7
ch4 = struct.unpack('<i', data[21+3:25+3])[0] # Amp[uV]
notify_one = struct.unpack('<i', data[25+3:29+3])[0]
notify_two = struct.unpack('<i', data[29+3:33+3])[0]
notify_three = struct.unpack('<i', data[33+3:37+3])[0]
if time_stamp == 0: if time_stamp == 0:
self._start_return_data = True self._start_return_data = True
@@ -1688,7 +1472,7 @@ class EISZeroOneDataDecoder(RecDataDecoder):
return None return None
if time_stamp != 0 and self._start_return_data == True: if time_stamp != 0 and self._start_return_data == True:
if (self._mode == 0 or self._mode == 5): if (self._mode == 0):
time_stamp, delta, ret_get_time_stamp = self.eis_get_time_stamp(time_stamp) time_stamp, delta, ret_get_time_stamp = self.eis_get_time_stamp(time_stamp)
else: else:
time_stamp, delta, ret_get_time_stamp = self.get_time_stamp(time_stamp) time_stamp, delta, ret_get_time_stamp = self.get_time_stamp(time_stamp)
@@ -1697,83 +1481,101 @@ class EISZeroOneDataDecoder(RecDataDecoder):
print("error timeStamp full data:", list(data), datetime.now(), '\n') print("error timeStamp full data:", list(data), datetime.now(), '\n')
return None return None
else: else:
if self.cali_coeff is not None and (self._mode == 0 or self._mode == 5): if self.cali_coeff is not None and self._mode == 0:
phase_para_a = []
phase_para_b = []
hsrtia_a = [] hsrtia_a = []
hsrtia_b = [] hsrtia_b = []
rolloff = [] hsrtia_c = []
phase_coeff, phase_offset, hsrtia_a, hsrtia_b, rolloff = self.cali_coeff[0] hsrtia_d = []
cutoff_freq, phase_para_a, phase_para_b, hsrtia_a, hsrtia_b, hsrtia_c, hsrtia_d = self.cali_coeff[0]
voltage_amp = round(self._ac_amp * 800 / 2047) # use UI value
if (self._freq_start > self._freq_stop):
self._freq_direction = 0
else:
self._freq_direction = 1
if (self._mode == 0 or self._mode == 5): if (self._mode == 0):
img = ch1 img = ch1
real = ch2 real = ch2
freq = ch3 freq = ch3
fre_idx = 0
voltage_amp = round(ch4 / 1000) # Amp[mV]
rolloff_cali = rolloff[gain]
voltage_mag = math.sqrt(img ** 2 + real ** 2) * (1 + freq ** 2 / rolloff_cali ** 2 / 1e4) voltage_mag = math.sqrt(img ** 2 + real ** 2) * (1 + freq ** 2 / cutoff_freq ** 2)
current = (voltage_mag ** 2 * hsrtia_a[gain] + voltage_mag * hsrtia_b[gain]) / 1e8 #[nA]
# if (gain == 3):
# current = hsrtia_a[gain] * math.exp(hsrtia_b[gain] * voltage_mag) + hsrtia_c[gain] * math.exp(hsrtia_d[gain] * voltage_mag)
# else:
current = voltage_mag ** 2 * hsrtia_a[gain] + voltage_mag * hsrtia_b[gain] + hsrtia_c[gain]
# print(current)
# print(voltage_mag)
# print(hsrtia_a[gain])
# print(hsrtia_b[gain])
# print(hsrtia_c[gain])
if (current != 0): if (current != 0):
# impedance[mOhm] = voltage_amp[mv] * 1000000 / 1.414213 / current[nA] #RMS=amp*SQRT(2), SQRT(2)=1.414213 # impedance = voltage_amp * 1000_000 / 1.414213 / current
impedance = voltage_amp * 707106.78 / current impedance = voltage_amp * 707106.78 / current
else: else:
impedance = 0 impedance = 0
raw_phase = math.atan2(img , real) * 180 / math.pi if (real > 0):
raw_phase = math.atan(img / real) * 180 / math.pi
elif (real == 0):
raw_phase = 90
else:
raw_phase = math.atan(img / real) * 180 / math.pi + 180
if (freq >= 1000000): # 10000 Hz if (freq >= 1000000): # 10000 Hz
fre_idx = 0 i = 0
elif (freq >= 10000): # 100 Hz elif (freq >= 10000): # 100 Hz
fre_idx = 1 i = 1
elif (freq >= 1000): # 10 Hz elif (freq >= 1000): # 10 Hz
fre_idx = 2 i = 2
elif (freq >= 1): # 0.01 Hz elif (freq >= 1): # 0.01 Hz
fre_idx = 3 i = 3
ideal_raw_phase = phase_coeff[gain][fre_idx] /1e10 * freq + phase_offset[gain][fre_idx] / 1e6 ideal_raw_phase = phase_para_a[i] /1e10 * freq + phase_para_b[i] / 1e6
phase = raw_phase - ideal_raw_phase phase = raw_phase - ideal_raw_phase
phase = phase % 180 if phase % 180<=90 else phase % 180-180
imag_after_cal = round(impedance * math.sin(phase * math.pi / 180)) if (self._first_phase_flag):
real_after_cal = round(impedance * math.cos(phase * math.pi / 180)) self._last_phase = phase
self._first_phase_flag = 0
elif (abs(phase - self._last_phase) >= 200):
phase -= 360
self._last_phase = phase
if self._show_data: imag_after_cal = impedance * math.sin(round(phase) * math.pi / 180)
if (self._mode == 0 or self._mode == 5): real_after_cal = impedance * math.cos(round(phase) * math.pi / 180)
print('|', '{:10}'.format(time_stamp),
'|', '{:5}'.format(delta),
'|', '{:5}'.format(ch1), #raw_img
'|', '{:5}'.format(ch2), #raw_real
'|', '{:8}'.format(ch3 * 10), '[mHz]', #Frequency [mHz]
'|', '{:5}'.format(cycle_number), #cycle
'|', '{:5}'.format(round(imag_after_cal)), '[Ohm]', #Z_imag [Ohm]
'|', '{:5}'.format(round(real_after_cal)), '[Ohm]', #Z_real [Ohm]
'|', '{:5}'.format(round(impedance)), '[Ohm]', #Impedance [Ohm]
'|', '{:5}'.format(round(phase*1000)), '[mdegree]', #Phase [millidegree]
'|', '{:5}'.format(round(current)), '[nA]', #Current [nA]
'|', '{:1}'.format(gain), #gain
'|', '{:1}'.format(finishMode), #finishMode
'@', str(self.device), '|', flush = True)
print('|', '{:10}'.format(time_stamp),
'|', '{:5}'.format(delta), if (self._mode == 0):
'|', '{:5}'.format(notify_one), # print('|', '{:10}'.format(time_stamp),
'|', '{:5}'.format(notify_two), # '|', '{:5}'.format(delta),
'|', '{:5}'.format(notify_three), # '|', '{:6}'.format(ch1),
'|', '{:5}'.format(voltage_amp), #amp[mV] # '|', '{:6}'.format(ch2),
'|', flush = True) # '|', '{:8}'.format(ch3 / 100),
pass # '|', '{:6}'.format(round(voltage_mag)),
else: # '|', '{:5}'.format(int(imag_after_cal)),
print('|', '{:10}'.format(time_stamp), # '|', '{:5}'.format(int(real_after_cal)),
'|', '{:5}'.format(delta), # '|', '{:5}'.format(round(impedance)),
'|', '{:5}'.format(ch1), # '|', '{:5}'.format(round(phase, 1)),
'|', '{:5}'.format(ch2), # '|', '{:5}'.format(round(current, 3)),
'|', '{:5}'.format(ch3), # '|', '{:1}'.format(gain),
'|', '{:5}'.format(cycle_number), # '|', '{:1}'.format(finishMode),
'|', '{:1}'.format(gain), # '@', str(self.device), '|')
'|', '{:1}'.format(finishMode), pass
'@', str(self.device), '|', flush = True) else:
pass # print('|', '{:10}'.format(time_stamp),
# '|', '{:5}'.format(delta),
# '|', '{:5}'.format(ch1),
# '|', '{:5}'.format(ch2),
# '|', '{:5}'.format(ch3),
# '|', '{:5}'.format(cycle_number),
# '|', '{:1}'.format(gain),
# '|', '{:1}'.format(finishMode),
# '@', str(self.device), '|')
pass
if finishMode == True: if finishMode == True:
print("finishMode full data:", list(data), datetime.now()) print("finishMode full data:", list(data), datetime.now())
@@ -1782,29 +1584,23 @@ class EISZeroOneDataDecoder(RecDataDecoder):
self._mode_stop = 0 self._mode_stop = 0
ret = RecordingData(self.device, int(time_stamp * 1000 / 2), 0) ret = RecordingData(self.device, int(time_stamp * 1000 / 2), 0)
if self._mode == 0 or self._mode == 5: if (self._mode == 0): #EIS Mode
ret.append_data(0, ch1) #raw_img ret.append_data(3, cycle_number)
ret.append_data(1, ch2) #raw_real 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(2, ch3 * 10) #Frequency [mHz]
ret.append_data(3, cycle_number) #cycle ret.append_data(4, round(imag_after_cal)) #Z_imag [Ohm]
ret.append_data(4, imag_after_cal) #Z_imag [Ohm] ret.append_data(5, round(real_after_cal)) #Z_real [Ohm]
ret.append_data(5, real_after_cal) #Z_real [Ohm]
ret.append_data(6, round(impedance)) #Impedance [Ohm] ret.append_data(6, round(impedance)) #Impedance [Ohm]
ret.append_data(7, round(phase*1000)) #Phase [millidegree] ret.append_data(7, round(phase)) #Phase [degree]
ret.append_data(8, round(current)) #Current [nA] ret.append_data(8, round(current)) #Current [nA]
ret.append_data(9, gain) #gain ret.append_data(9, gain) #Gain Level
#debug data
ret.append_data(10, notify_one)
ret.append_data(11, notify_two)
ret.append_data(12, notify_three)
ret.append_data(13, voltage_amp) #amp[mV]
else: #CV Mode else: #CV Mode
ret.append_data(3, cycle_number)
ret.append_data(0, ch1) #Iin [nA] ret.append_data(0, ch1) #Iin [nA]
ret.append_data(1, ch2) #Vset [nV] ret.append_data(1, ch2) #Vset [nV]
ret.append_data(2, ch3) #Vout [nV] ret.append_data(2, ch3) #Vout [nV]
ret.append_data(3, cycle_number)
if cycle_number != self._cycle_number: if cycle_number != self._cycle_number:
# notify cycle_number change # notify cycle_number change
+14 -1
View File
@@ -651,9 +651,17 @@ class DeviceInstruction:
VIS_DEVICE_DONE = 0x20 VIS_DEVICE_DONE = 0x20
"""identify device done""" """identify device done"""
VIS_CC_ZERO = 0x40
VIS_ASK = 0x30
"""ask in virtual instruction"""
VIS_STI = 0xC0 VIS_STI = 0xC0
"""stimulation on virtual instruction""" """stimulation on virtual instruction"""
VIS_FUH = 0x90
"""flush virtual instruction"""
VIS_INT = 0x60 VIS_INT = 0x60
"""interrupt virtual instruction""" """interrupt virtual instruction"""
@@ -724,6 +732,7 @@ class DeviceCommonInstruction:
STOP_STIMULATE = "stop_stimulate" STOP_STIMULATE = "stop_stimulate"
VIS_DEVICE_DETECT = 'VIS_DEVICE_DETECT' VIS_DEVICE_DETECT = 'VIS_DEVICE_DETECT'
VIS_DEVICE_DONE = 'VIS_DEVICE_DONE' VIS_DEVICE_DONE = 'VIS_DEVICE_DONE'
VIS_CC_ZERO = 'VIS_CC_ZERO'
CIS_VOLT = 'CIS_VOLT' CIS_VOLT = 'CIS_VOLT'
CIS_VERSION = 'CIS_VERSION' CIS_VERSION = 'CIS_VERSION'
@@ -738,15 +747,19 @@ class DeviceCommonInstruction:
elif instruction == cls.START: elif instruction == cls.START:
return '_data_format("TDC4VAF2")', '_notify(True)', 'VIS_STI', '_sync(True)' return '_data_format("TDC4VAF2")', '_notify(True)', 'VIS_STI', '_sync(True)'
elif instruction == cls.INTERRUPT: elif instruction == cls.INTERRUPT:
return '_notify(False)', 'VIS_INT', '_sync(False)' return '_sync(False)', '_notify(False)', 'VIS_INT'
elif instruction == cls.CLOSE: elif instruction == cls.CLOSE:
return '_sync(False)', '_notify(False)', 'VIS_INT' return '_sync(False)', '_notify(False)', 'VIS_INT'
elif instruction == cls.FLUSH:
return 'VIS_FUH',
elif instruction == cls.CALL: elif instruction == cls.CALL:
return 'VIS_CAL', return 'VIS_CAL',
elif instruction == cls.VIS_DEVICE_DETECT: elif instruction == cls.VIS_DEVICE_DETECT:
return 'VIS_DEVICE_DETECT', return 'VIS_DEVICE_DETECT',
elif instruction == cls.VIS_DEVICE_DONE: elif instruction == cls.VIS_DEVICE_DONE:
return 'VIS_DEVICE_DONE', return 'VIS_DEVICE_DONE',
elif instruction == cls.VIS_CC_ZERO:
return 'VIS_CC_ZERO',
elif instruction == cls.RECORD: elif instruction == cls.RECORD:
return 'VIS_STARTR', return 'VIS_STARTR',
elif instruction == cls.RECORD_ALL: elif instruction == cls.RECORD_ALL:
+2 -15
View File
@@ -4,7 +4,6 @@ from biopro.util.text import part_suffix
from . import * from . import *
from .expression import * from .expression import *
import json
class DeviceParameterError(RuntimeError): class DeviceParameterError(RuntimeError):
__slots__ = () __slots__ = ()
@@ -310,8 +309,6 @@ class ParameterValueDomain(ParameterDomain, metaclass=abc.ABCMeta):
return f return f
def valid_para(self, value: Any) -> bool: def valid_para(self, value: Any) -> bool:
if isinstance(value, list):
return True
f, t = self.range f, t = self.range
return f <= float(value) < t return f <= float(value) < t
@@ -492,12 +489,8 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
@classmethod @classmethod
def parse(cls, expr: Union[int, str]) -> 'ParameterCollectionValueOperator': def parse(cls, expr: Union[int, str]) -> 'ParameterCollectionValueOperator':
# parse epression then create value_operator
if isinstance(expr, int): if isinstance(expr, int):
return SingleValueOperator(None, expr) return SingleValueOperator(None, expr)
elif isinstance(expr, list):
return CollectValueOperator((0, len(expr)), expr)
elif expr == '*': elif expr == '*':
return AddAllValueOperator return AddAllValueOperator
@@ -505,10 +498,6 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
elif expr == ':=': elif expr == ':=':
return CollectValueOperator((None, None), []) return CollectValueOperator((None, None), [])
elif '[' in expr and ']' in expr:
v = json.loads(expr)
return CollectValueOperator((0, len(v)), v)
elif '=' in expr: elif '=' in expr:
a, v = part_suffix(expr, '=') a, v = part_suffix(expr, '=')
@@ -519,7 +508,6 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
return CollectValueOperator((None, None), v) return CollectValueOperator((None, None), v)
else: else:
# get list length (old way)
a, b = part_suffix(a, ':') a, b = part_suffix(a, ':')
a = int(a) if len(a) else None a = int(a) if len(a) else None
b = int(b) if len(b) else None b = int(b) if len(b) else None
@@ -614,8 +602,8 @@ class ParameterCollectionDomain(ParameterDomain, metaclass=abc.ABCMeta):
def __init__(self, domain: ParameterDomain): def __init__(self, domain: ParameterDomain):
super().__init__() super().__init__()
# if isinstance(domain, ParameterCollectionDomain): if isinstance(domain, ParameterCollectionDomain):
# raise RuntimeError('cannot compose with parameter collection domain') raise RuntimeError('cannot compose with parameter collection domain')
self._domain = domain self._domain = domain
@@ -737,7 +725,6 @@ class ParameterListDomain(ParameterCollectionDomain):
if oper == AddAllValueOperator: if oper == AddAllValueOperator:
raise ValueError("list parameter cannot accept '*' (all) value") raise ValueError("list parameter cannot accept '*' (all) value")
# d: domain, sz: lsit length, i: slice(a, b), v: list value
d = self.element_domain d = self.element_domain
sz = len(target) sz = len(target)
i = oper.index(sz) i = oper.index(sz)
+3 -7
View File
@@ -119,10 +119,7 @@ class DeviceConfigurationEncoder:
e.write(b'L\0') e.write(b'L\0')
else: else:
parameter_type = cls._value_type(max(value)) parameter_type = cls._value_type(value[0])
for v in value:
if isinstance(v, int) and v < 0:
parameter_type = parameter_type.lower()
e.write(b'L') e.write(b'L')
e.write(parameter_type.encode()) e.write(parameter_type.encode())
e.u8(sz) e.u8(sz)
@@ -156,8 +153,7 @@ class DeviceConfigurationEncoder:
if isinstance(value, bool): if isinstance(value, bool):
e.u8(1 if value else 0) e.u8(1 if value else 0)
elif isinstance(value, str) or isinstance(value, list): elif isinstance(value, str):
value = str(value)
e.encode_string(value) e.encode_string(value)
elif isinstance(value, int): elif isinstance(value, int):
@@ -174,7 +170,7 @@ class DeviceConfigurationEncoder:
if isinstance(value, bool): if isinstance(value, bool):
return '?' return '?'
elif isinstance(value, str) or isinstance(value, list): elif isinstance(value, str):
return 'p' return 'p'
elif isinstance(value, int): elif isinstance(value, int):
+2 -7
View File
@@ -283,7 +283,6 @@ class InternalInstruction(SingleInstruction):
PREDEFINED_SET = '_set' PREDEFINED_SET = '_set'
PREDEFINED_CDR = '_cdr' PREDEFINED_CDR = '_cdr'
PREDEFINED_DISABLE_CACHE = '_disable_cache' PREDEFINED_DISABLE_CACHE = '_disable_cache'
PREDEFINED_IDLE = '_idle'
PREDEFINED = ( PREDEFINED = (
PREDEFINED_SLEEP, PREDEFINED_SLEEP,
@@ -295,7 +294,6 @@ class InternalInstruction(SingleInstruction):
PREDEFINED_SET, PREDEFINED_SET,
PREDEFINED_CDR, PREDEFINED_CDR,
PREDEFINED_DISABLE_CACHE, PREDEFINED_DISABLE_CACHE,
PREDEFINED_IDLE,
) )
__slots__ = ('_expr', '_para') __slots__ = ('_expr', '_para')
@@ -794,7 +792,6 @@ class InstructionDataContent(InstructionContent):
offset += 1 offset += 1
else: else:
buffer.extend([0]*(self._width.size-len(buffer)))
for i in range(self._width.size): for i in range(self._width.size):
if self._width.little_endian: if self._width.little_endian:
value |= (buffer[offset] << (1 * i)) value |= (buffer[offset] << (1 * i))
@@ -1271,15 +1268,14 @@ class InstructionTable(DictNode['Instruction']):
instruction: str): instruction: str):
"""call instruction. chain context with :class:`DeviceInternalInstructionScope` """call instruction. chain context with :class:`DeviceInternalInstructionScope`
:param master: DeviceManager :param master: master device
:param device: CompletedDevice :param device: device
:param context: scope :param context: scope
:param instruction: instruction name :param instruction: instruction name
:raises InstructionNotFound: If instruction not found. :raises InstructionNotFound: If instruction not found.
""" """
try: try:
# ex. start
ins = self[instruction] ins = self[instruction]
except KeyError as e: except KeyError as e:
@@ -1288,7 +1284,6 @@ class InstructionTable(DictNode['Instruction']):
else: else:
context = context.chain(InstructionTableScope(self)) context = context.chain(InstructionTableScope(self))
# ex. _data_format, _sync, _notify
for single in ins.eval_instruction(context): for single in ins.eval_instruction(context):
if isinstance(single, InternalInstruction): if isinstance(single, InternalInstruction):
if not single.handle_internal_instruction(master, device, context): if not single.handle_internal_instruction(master, device, context):
+20 -57
View File
@@ -510,10 +510,9 @@ class DeviceParameter(JsonSerialize):
:param para: parameter name :param para: parameter name
:param oper: operator expression :param oper: operator expression
""" """
# parse operator return CollectValueOperator instance
if not isinstance(oper, ParameterCollectionValueOperator): if not isinstance(oper, ParameterCollectionValueOperator):
oper = ParameterCollectionValueOperator.parse(oper) oper = ParameterCollectionValueOperator.parse(oper)
table = self._library.parameter_table table = self._library.parameter_table
info = table[para] info = table[para]
domain = info.domain domain = info.domain
@@ -529,7 +528,6 @@ class DeviceParameter(JsonSerialize):
old = list(target) old = list(target)
try: try:
# replace target with new oper
domain.oper_para(target, oper, self._context) domain.oper_para(target, oper, self._context)
except RuntimeError as e: except RuntimeError as e:
raise IllegalParameterValueError(para, str(oper)) from e raise IllegalParameterValueError(para, str(oper)) from e
@@ -968,7 +966,7 @@ class CompletedDevice(Device):
""" """
__slots__ = ('_master', '_device_id', '_device', '_library', '_context', __slots__ = ('_master', '_device_id', '_device', '_library', '_context',
'_parameter', '_configuration', '_lock', '_feature_mask', '_parameter', '_configuration', '_lock', '_feature_mask',
'_cache_battery', '_cache_battery_timestamp', '_coeff', '_status', '_occupied_by_project') '_cache_battery', '_cache_battery_timestamp', '_coeff')
def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device): def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device):
""" """
@@ -978,17 +976,11 @@ class CompletedDevice(Device):
:param device_id: device id :param device_id: device id
:param device: slave device implementation :param device: slave device implementation
""" """
# DeviceManager
self._master = master self._master = master
self._device = device self._device = device
self._device_id = device_id self._device_id = device_id
self._library = library self._library = library
# device status ( 0: idle, 1: working, 2: error, 3: power off)
self._status = 0
# device be occupied by project
self._occupied_by_project = None
# context # context
s1 = library.constant s1 = library.constant
s2 = DeviceScope(self) s2 = DeviceScope(self)
@@ -1016,10 +1008,6 @@ class CompletedDevice(Device):
def device_id(self) -> int: def device_id(self) -> int:
return self._device_id return self._device_id
@property
def memory_board(self) -> int:
return self._device_id
@property @property
def device_name(self) -> str: def device_name(self) -> str:
return self._device.device_name return self._device.device_name
@@ -1032,10 +1020,6 @@ class CompletedDevice(Device):
def mac_address(self) -> ADDRESS: def mac_address(self) -> ADDRESS:
return self._device.mac_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).upper()
@property @property
def device(self) -> Device: def device(self) -> Device:
return self._device return self._device
@@ -1072,26 +1056,11 @@ class CompletedDevice(Device):
return self._library return self._library
# device lock # device lock
@property @property
def lock(self) -> DeviceLock: def lock(self) -> DeviceLock:
return self._lock return self._lock
@property
def status(self) -> int:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
@property
def occupied_by_project(self) -> str:
return self._occupied_by_project
@occupied_by_project.setter
def occupied_by_project(self, new_occupied_by_project):
self._occupied_by_project = new_occupied_by_project
# device parameter getter/setter # device parameter getter/setter
def parameters(self) -> List[str]: def parameters(self) -> List[str]:
@@ -1113,13 +1082,6 @@ class CompletedDevice(Device):
:return: parameter V value :return: parameter V value
""" """
return self._configuration.get_parameter(name, False) return self._configuration.get_parameter(name, False)
def set_multi_parameters(self, parameter):
if len(parameter) == 0:
return
for (name, value) in parameter[0].items():
if name != 'target':
self.set_parameter(name, value)
def set_parameter(self, name: str, value: Union[int, str]): def set_parameter(self, name: str, value: Union[int, str]):
"""replace parameter value with *value* """replace parameter value with *value*
@@ -1140,11 +1102,6 @@ class CompletedDevice(Device):
self._parameter.set_parameter(name, value) self._parameter.set_parameter(name, value)
# raise RuntimeError('illegal parameter value : ' + value) from e # raise RuntimeError('illegal parameter value : ' + value) from e
else: else:
if name == 'MODE':
if self.library_name.startswith('Elite_EDC') or 'EIS' in self.library_name:
self.set_parameter('CTRL_HIGH_Z_15', self.get_parameter('HIGHZ_TABLE')[value])
self.set_parameter('TIME_DURATION', 0)
self._parameter.set_parameter(name, value) self._parameter.set_parameter(name, value)
on_change = info.on_change on_change = info.on_change
@@ -1153,8 +1110,7 @@ class CompletedDevice(Device):
self._master.log_warn('on_change instruction not found : ' + on_change) self._master.log_warn('on_change instruction not found : ' + on_change)
else: else:
if self._status == 1: self.call_instruction(on_change)
self.call_instruction(on_change)
# device instruction call # device instruction call
@@ -1297,23 +1253,16 @@ class CompletedDevice(Device):
def as_json(self) -> JSON_OBJECT: def as_json(self) -> JSON_OBJECT:
return { return {
'id': self.device_id,
'name': self.device_name,
'status': self._status,
'device_id': self.device_id, 'device_id': self.device_id,
'device_name': self.device_name, 'device_name': self.device_name,
'device_address': list(self.mac_address), '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, 'battery': self.battery,
'parent': self.parent, 'parent': self.parent,
'recording_file_name': self.recording_file_name, '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_name': self._library.name,
'library_version': str(self._library.version), 'library_version': str(self._library.version),
'occupied_by_project': self._occupied_by_project,
'configuration': self.configuration.as_json(list_hide=True), 'configuration': self.configuration.as_json(list_hide=True),
} }
@@ -1403,6 +1352,20 @@ class DebugDevice(CompletedDevice):
def read_command_return_data(self) -> Optional[bytes]: def read_command_return_data(self) -> Optional[bytes]:
return None return None
def send_command(self, ins_type: int, ins_oper: int, *instruction: int):
length = len(instruction)
print('instruction send :',
'%02X' % ((ins_type & 0xF0) | (self.device_id & 0x0F)),
'%02X' % ((ins_oper & 0xF0) | (length & 0x0F)),
' '.join(map(lambda v: '%02X' % v, instruction)))
def send_instruction(self, ins_type: int, ins_oper: int, *instruction: int):
length = len(instruction)
print('instruction send :',
'%02X' % ((ins_type & 0xF0) | (self.device_id & 0x0F)),
'%02X' % ((ins_oper & 0xF0) | (length & 0x0F)),
' '.join(map(lambda v: '%02X' % v, instruction)))
@property @property
def battery(self) -> int: def battery(self) -> int:
return 100 return 100
+10 -14
View File
@@ -3697,6 +3697,7 @@ class CC2650Central(LoggerFlag):
def recv(self, timeout: Optional[float] = None) -> Optional[list]: def recv(self, timeout: Optional[float] = None) -> Optional[list]:
packet = self._recv_byte() packet = self._recv_byte()
# print("packet = ", packet)
if packet is None: if packet is None:
return None return None
@@ -3708,8 +3709,6 @@ class CC2650Central(LoggerFlag):
def _recv_byte(self) -> Optional[int]: def _recv_byte(self) -> Optional[int]:
ret = self._recv_bytes(1) ret = self._recv_bytes(1)
# if ret is not None:
# print('packet = {0}'.format(hex(ret[0]).upper()))
return ret[0] if ret is not None else None return ret[0] if ret is not None else None
def _recv_bytes(self, size: int = 1) -> Union[None, bytes]: def _recv_bytes(self, size: int = 1) -> Union[None, bytes]:
@@ -3725,28 +3724,27 @@ class CC2650Central(LoggerFlag):
return None return None
def _recv_event(self, timeout: Optional[float] = 1) -> Optional[list]: def _recv_event(self, timeout: Optional[float] = 1) -> Optional[list]:
packet = self._recv_byte() code = self._recv_byte()
# print("code = ", code)
if packet is None: if code is None:
return None return None
length = self._recv_byte() length = self._recv_byte()
len_b = _struct.pack("B", length) # print('code::',code,'length::',length)
data = b''
_start = _time() _start = _time()
data = b''
while len(data) < length: while len(data) < length:
packets = self._recv_bytes(length - len(data)) ret = self._recv_bytes(length - len(data))
# hex_packets = ''.join(format(i, '02x') for i in packets) # print("ret = ", ret)
# print("packets = ", hex_packets.upper())
if packets is not None: if ret is not None:
data += packets data += ret
elif timeout is not None and _time() - _start > timeout: elif timeout is not None and _time() - _start > timeout:
raise RecvTimeout() raise RecvTimeout()
data = len_b + data
self._interface.flush() self._interface.flush()
return list(data) return list(data)
@@ -3762,7 +3760,5 @@ class CC2650Central(LoggerFlag):
raise RecvTimeout() raise RecvTimeout()
else: else:
uart_data_hex = ''.join(format(i, '02X') for i in uart_data)
# print('uart_data: 0x{0}'.format(uart_data_hex.upper()))
return uart_data return uart_data
+68 -173
View File
@@ -13,7 +13,7 @@ MSM_REG_WRITE = 0x01
MEM_INS_WRITE = 0x02 MEM_INS_WRITE = 0x02
MEM_INS_READ = 0x03 MEM_INS_READ = 0x03
MEM_REG_READ = 0x05 MEM_REG_READ = 0x05
DEFAULT_REGISTER_VALUE = 0b0100_0001 # 0x41 DEFAULT_REGISTER_VALUE = 0b0100_0011 # 67
MEM_SIZE = 0x1000 MEM_SIZE = 0x1000
_SLEEP_TIME_ = 0.001 _SLEEP_TIME_ = 0.001
@@ -24,10 +24,8 @@ def zero_buffer(size: int) -> List[int]:
return [0] * size return [0] * size
class MultiExtMemSpiInterface(LowLevelHardwareInterface): class MultiExtMemSpiInterface(LowLevelHardwareInterface):
MEM_INS_MARKED1 = [MEM_INS_WRITE, 0, 2, 0x5A, 0xA5] MEM_INS_MARKED = [MEM_INS_WRITE, 0, 2, 1, 1]
MEM_INS_MARKED2 = [MEM_INS_WRITE, 0, 6, 0x5A, 0xA5] MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
MEM_INS_MARKED3 = [MEM_INS_WRITE, 0, 10, 0x5A, 0xA5]
# MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer_header', '_tx_buffer_data', __slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer_header', '_tx_buffer_data',
'pin_busy', 'pin_mem_req', 'pin_mem_sel', 'pin_ram_sel', 'pin_busy', 'pin_mem_req', 'pin_mem_sel', 'pin_ram_sel',
@@ -35,7 +33,7 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
'_pin_ram_sel_value', '_pin_mem_sel_value', '_pin_mem_req_value', '_pin_ram_sel_value', '_pin_mem_sel_value', '_pin_mem_req_value',
'_read_green_times','_read_red_times', '_read_green_times','_read_red_times',
'_elite_data_len', '_mem_header_len', '_mem_tailer_len', '_single_data_len', '_elite_data_len', '_mem_header_len', '_mem_tailer_len', '_single_data_len',
'_head_wrong_cnt', '_pin_busy_value') '_head_wrong_cnt')
def __init__(self, def __init__(self,
select: Selector, select: Selector,
@@ -46,15 +44,15 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._elite_data_len = 40 self._elite_data_len = 40
self._mem_header_len = 3 self._mem_header_len = 3
self._mem_tailer_len = 6 self._mem_tailer_len = 8
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
# buffer # buffer
self._tx_buffer_header = [0] * 64 self._tx_buffer_header = [0] * 11
self._tx_buffer_data = [0] * (self._single_data_len * 10 + 3) self._tx_buffer_data = [0] * (self._single_data_len * 10 + 3)
# memory control pin # memory control pin
self.pin_busy: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_BZY) self.pin_busy = OutputPin.get_used(P3Pin.MEM_BZY, True)
self.pin_mem_req = OutputPin.get_used(P3Pin.MEM_REQ, False) self.pin_mem_req = OutputPin.get_used(P3Pin.MEM_REQ, False)
self.pin_mem_sel = OutputPin.get_used(P3Pin.MEM_RST, True) # MEM_RST -> actually which memory board is assign self.pin_mem_sel = OutputPin.get_used(P3Pin.MEM_RST, True) # MEM_RST -> actually which memory board is assign
self.pin_ram_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL) # MEM_SEL -> actually is RAM_SEL, which RAM is assign self.pin_ram_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL) # MEM_SEL -> actually is RAM_SEL, which RAM is assign
@@ -62,7 +60,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._pin_ram_sel_value = [bool(self.pin_ram_sel) for _ in range(Selector.SIZE)] self._pin_ram_sel_value = [bool(self.pin_ram_sel) for _ in range(Selector.SIZE)]
self._pin_mem_sel_value = [bool(self.pin_mem_sel) for _ in range(Selector.SIZE)] self._pin_mem_sel_value = [bool(self.pin_mem_sel) for _ in range(Selector.SIZE)]
self._pin_mem_req_value = [bool(self.pin_mem_req) for _ in range(Selector.SIZE)] self._pin_mem_req_value = [bool(self.pin_mem_req) for _ in range(Selector.SIZE)]
self._pin_busy_value = [bool(self.pin_busy) for _ in range(Selector.SIZE)]
self._read_green_times = 0 self._read_green_times = 0
self._read_red_times = 0 self._read_red_times = 0
@@ -77,8 +74,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
def set_pin_mem_req(self, value: bool): def set_pin_mem_req(self, value: bool):
channel = self.select 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.output(value)
self._pin_mem_req_value[channel] = value self._pin_mem_req_value[channel] = value
@@ -99,14 +94,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._pin_ram_sel_value[channel] = True self._pin_ram_sel_value[channel] = True
return self._pin_ram_sel_value[channel] return self._pin_ram_sel_value[channel]
def get_pin_busy(self):
channel = self.select
if self.pin_busy.input() == 0:
self._pin_busy_value[channel] = False
else:
self._pin_busy_value[channel] = True
return self._pin_busy_value[channel]
@property @property
def select(self) -> int: def select(self) -> int:
return self._selector.channel return self._selector.channel
@@ -178,56 +165,8 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
print("read red data times", self._read_red_times) print("read red data times", self._read_red_times)
return data return data
def _compare_green_data_addr_and_flag(self, data: Union[bytes, List[int]], device: int):
green_data_section1 = data[3:7]
green_data_section2 = data[7:11]
green_data_section3 = data[11:15]
green_data_section = []
if green_data_section1 == green_data_section2:
green_data_section = green_data_section1
elif(green_data_section2 == green_data_section3 or green_data_section1 == green_data_section3):
print("green data not equal: = ", data[3:15])
print("green data print:", data, device, datetime.now())
green_data_section = green_data_section3
else:
print("green data not equal: = ", data[3:15])
print("green data print:", data, device, datetime.now())
green_data_section = green_data_section3 # use last data
return green_data_section
def _print_ram_all_data(self):
addr = 0
red_length = int(7000 / 2)
tx_temp = [0] * (red_length + 3)
while True:
tx_temp[0] = MEM_INS_READ
tx_temp[1] = ((addr >> 8) & 0xFF)
tx_temp[2] = (addr & 0xFF)
ram_data = []
ram_data = self._spi.send_byte(tx_temp)
ram_data[0:3] = [255, 255, 255]
print(list(ram_data), len(ram_data), addr)
addr += len(ram_data) - 3
if (7000 < addr + len(ram_data)):
del ram_data
break
print()
return
def recv_memory(self, device: int) -> Optional[bytes]: def recv_memory(self, device: int) -> Optional[bytes]:
# self.pin_busy.output(False) self.pin_busy.output(False)
# print('mem_req==ram_sel,[', self._pin_mem_req_value[device], ',', self._pin_ram_sel_value[device], ']')
rx = [] rx = []
@@ -252,7 +191,7 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1 self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
if (self._head_wrong_cnt[device] <= 5): # print 5 times if (self._head_wrong_cnt[device] <= 5): # print 5 times
print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times') print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
print(list(data)) print(list(data[0:7]))
data[0:3] = [255, 255, 255] data[0:3] = [255, 255, 255]
# ---------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------
@@ -271,13 +210,13 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1 # self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
# if (self._head_wrong_cnt[device] < 10): # if (self._head_wrong_cnt[device] < 10):
# print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times') # print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
# print(list(data_first)) # print(list(data_first[0:7]))
# if (data_second[0] != 255 or data_second[1] != 255 or data_second[2] != 255): # if (data_second[0] != 255 or data_second[1] != 255 or data_second[2] != 255):
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1 # self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
# if (self._head_wrong_cnt[device] < 10): # if (self._head_wrong_cnt[device] < 10):
# print('data_second[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times') # print('data_second[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
# print(list(data_second)) # print(list(data_second[0:7]))
# if (data_first[3:] == data_second[3:]): # if (data_first[3:] == data_second[3:]):
# data = data_first # data = data_first
@@ -289,50 +228,59 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
# data = self.compare_green_data(data_first, data_second, data_third, len(tx_h)) # data = self.compare_green_data(data_first, data_second, data_third, len(tx_h))
# ---------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------
green_data_section = self._compare_green_data_addr_and_flag(data, device) # debug use
# green_data = []
# green_data = data
# print("_[Debug] @ spi green data = ", green_data)
length = (green_data_section[0] << 8) | green_data_section[1] length = (data[3] << 8) | data[4]
header = data[15] header = data[7]
elite_data_len = data[17] elite_data_len = data[9]
if length <= 12:
if length < 12:
print("length < 12")
print("green data print:", data, device, datetime.now())
# if length == 0:
# self._print_ram_all_data()
if length <= 4:
# print("length <= 4")
# print("green data print:", data, device, datetime.now())
return None return None
if green_data_section[2] != 0xA5 or green_data_section[3] != 0x5A: if data[5] != 0 or data[6] != 0:
# give a default length if the data header did not update # give a default length if the data header did not update
print("green data: is not [0xA5, 0x5A], = ", data[5:7]) length = 7000
print("green data: is not [0,0], = ", data[5:7])
print("green data print:", data, device, datetime.now()) print("green data print:", data, device, datetime.now())
return None return None
# print('data=', list(data)) if (length >= 7000):
# print('Ram:', data[62])
if (length >= 4000):
flag_print = True flag_print = True
print("green data: big length:", length) print("green data: big length:", length)
if (header != 255): if (header != 255):
flag_print = True flag_print = True
print("green data: header is not 255: ", header) print("green data: header is not 255: ", header)
# self._print_ram_all_data()
if (elite_data_len != self._elite_data_len): if (elite_data_len != self._elite_data_len):
flag_print = True flag_print = True
print("green data: length is not", self._elite_data_len, ": ", elite_data_len) print("green data: length is not", self._elite_data_len, ": ", elite_data_len)
if (flag_print): if (flag_print):
print("green data print:", data, device, datetime.now(), '\n') print("green data print:", data, device, datetime.now())
address = 12 # neulive
# address += len(data) - 11
# tx_d = self._tx_buffer_data
# red_length = len(tx_d)
# elite read all data of ram
# address = 4
# red_length = length - 1
# tx_d = [0] * red_length
address = 4
red_length = int(length / 2) red_length = int(length / 2)
tx_d = [0] * (red_length + 3) tx_d = [0] * red_length
# elite read len(_tx_buffer_data) byte
# address = 4
# tx_d = self._tx_buffer_data
while True: while True:
tx_d[0] = MEM_INS_READ tx_d[0] = MEM_INS_READ
@@ -371,67 +319,29 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
address += len(data) - 3 address += len(data) - 3
del data del data
# last_index = 0 # print("_[Debug] @ spi recv data rx = ")
# print("_[Debug] @ spi recv data rx, ram_select:", self._pin_ram_sel_value[device], ",", datetime.now())
# for i in range(0, len(rx), self._single_data_len): # for i in range(0, len(rx), self._single_data_len):
# last_index = i # print(rx[i:i+self._single_data_len])
# # print(rx[i:i+self._single_data_len])
# if i == 0:
# print(rx[0:8], 'ram:', rx[47])
# if last_index != 0:
# print(rx[last_index:last_index+8], 'ram:', rx[last_index+47])
# print() # print()
# /* # /*
# * red data formate: # * red data formate:
# * ramHdr, ramHdr, ramHdr, (3B) # * ramHdr, ramHdr, ramHdr, (3B)
# * 255, #, data_length, (3B) # * 255, #, data_length, (3B)
# * data, (40B) # * data, (20B)
# * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B) # * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B)
# * check_num, (1B) # * 255, #, data_length, (3B)
# * */ # * */
# read again if check num is wrong
index = 0
check_number_print = False
for i in range(0, len(rx), self._single_data_len):
check_sum = sum(rx[i : i + self._single_data_len - 1]) & 0b11111111
# print(check_sum, rx[i + self._single_data_len - 1])
if (check_sum != rx[i + self._single_data_len - 1]):
# print('check_sum wrong, origin value:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
tx_d = [0] * (self._single_data_len + 3)
address = 12 + self._single_data_len * index
tx_d[0] = MEM_INS_READ
tx_d[1] = ((address >> 8) & 0xFF)
tx_d[2] = (address & 0xFF)
data = []
data = self._spi.send_byte(tx_d)
data[0:3] = [255, 255, 255]
rx[i : i + self._single_data_len] = data[3:]
print('check_sum wrong, read again:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
check_number_print = True
index = index + 1
if check_number_print:
print('check_sum wrong:', device, datetime.now())
for i in range(0, len(rx), self._single_data_len):
print(rx[i:i+self._single_data_len])
print()
# mark read # mark read
self._spi.send_byte(self.MEM_INS_MARKED1) self._spi.send_byte(self.MEM_INS_MARKED)
self._spi.send_byte(self.MEM_INS_MARKED2)
self._spi.send_byte(self.MEM_INS_MARKED3)
except BaseException as e: except BaseException as e:
print(e) print(e)
# finally: finally:
# # print("\n") # print("\n")
# self.pin_busy.output(True) self.pin_busy.output(True)
return bytes(rx) return bytes(rx)
@@ -457,16 +367,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._spi.send_byte(tx) self._spi.send_byte(tx)
def test_ram(self, channel: int):
spi_MOSI = [MEM_INS_WRITE, 0, 0, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
self._spi.send_byte(spi_MOSI)
print('device:', channel, 'spi_MOSI:',spi_MOSI)
spi_MISO = [0] * len(spi_MOSI)
spi_MISO[0:3] = [MEM_INS_READ, 0, 0]
spi_MISO = self._spi.send_byte(spi_MISO)
print('device:', channel, 'spi_MISO:', spi_MISO)
class ExtMemManager: class ExtMemManager:
def __init__(self, ext_mem: MultiExtMemSpiInterface): def __init__(self, ext_mem: MultiExtMemSpiInterface):
self._ext_mem = ext_mem self._ext_mem = ext_mem
@@ -503,31 +403,28 @@ class ExtMemManager:
ret[channel] = tuple(r) ret[channel] = tuple(r)
print('ret=', ret)
return ret return ret
@staticmethod @staticmethod
def is_memory_test_fail(result: Tuple[Optional[int], Optional[int]], channel: int) -> int: def is_no_device(result: Tuple[Optional[int], Optional[int]]) -> bool:
r1, r2 = result r1, r2 = result
r1_check = False return (r1 is None or r2 is None) and (r1 is None or r1 == 0) and (r2 is None or r2 == 0)
r2_check = False
if r1 is not None and r1 != 0: @staticmethod
if (r1 & 0b11000001 == DEFAULT_REGISTER_VALUE & 0b11000001): def is_memory_test_fail(result: Tuple[Optional[int], Optional[int]]) -> int:
r1_check = True r1, r2 = result
print('device:', channel, 'ram0 ready')
if r2 is not None and r2 != 0:
if (r2 & 0b11000001 == DEFAULT_REGISTER_VALUE & 0b11000001):
r2_check = True
print('device:', channel, 'ram1 ready')
print('--------------------')
if r1_check and r2_check: if r1 is None or r2 is None:
return 0
else:
return 1 return 1
if r1 is not None and r1 > 0 and r1 != DEFAULT_REGISTER_VALUE:
return 2
if r2 is not None and r2 > 0 and r2 != DEFAULT_REGISTER_VALUE:
return 3
return 0
def get_available_channel(self, result: List[Tuple[Optional[int], Optional[int]]] = None) -> List[int]: def get_available_channel(self, result: List[Tuple[Optional[int], Optional[int]]] = None) -> List[int]:
if result is None: if result is None:
result = self.get_ext_mem_register() result = self.get_ext_mem_register()
@@ -535,14 +432,12 @@ class ExtMemManager:
ret = [] ret = []
for channel, result in enumerate(result): for channel, result in enumerate(result):
if self.is_memory_test_fail(result, channel) != 0: if self.is_no_device(result):
continue
if self.is_memory_test_fail(result) != 0:
continue continue
ret.append(channel) ret.append(channel)
# test ram
# for channel in self._ext_mem.foreach():
# if channel == 4 or channel == 5:
# self._ext_mem.test_ram(channel)
return ret return ret
+1 -3
View File
@@ -28,7 +28,7 @@ class UARTInterface(LowLevelHardwareInterface):
self._serial = serial.Serial(self._port, self._serial = serial.Serial(self._port,
baudrate=self._baudrate, baudrate=self._baudrate,
timeout=0, timeout=0,
writeTimeout=1) writeTimeout=0.01)
# deprecate function name which change at version 3.0 # deprecate function name which change at version 3.0
self._serial.flushInput() self._serial.flushInput()
self._serial.flushOutput() self._serial.flushOutput()
@@ -53,8 +53,6 @@ class UARTInterface(LowLevelHardwareInterface):
:param data: raw byte instruction :param data: raw byte instruction
:raises SerialTimeoutException: :raises SerialTimeoutException:
""" """
data_hex = ''.join(format(i, '02X') for i in data)
# print('send_byte: 0x{0}'.format(data_hex.upper()))
self._serial.write(data) self._serial.write(data)
def recv_byte(self, size: int) -> Optional[bytes]: def recv_byte(self, size: int) -> Optional[bytes]:
+4 -4
View File
@@ -118,13 +118,13 @@ class Selector:
self._p2.output(p[2]) self._p2.output(p[2])
# if (value == 4 and self._last_sel != 6): # if (value == 4 and self._last_sel != 6):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now()) # print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
# elif (value == 5 and self._last_sel != 4): # elif (value == 5 and self._last_sel != 4):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now()) # print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
# elif (value == 7 and self._last_sel != 5): # elif (value == 7 and self._last_sel != 5):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now()) # print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
# elif (value == 6 and self._last_sel != 7): # elif (value == 6 and self._last_sel != 7):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now()) # print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
self._last_sel = value self._last_sel = value
View File
-34
View File
@@ -1,34 +0,0 @@
class Action():
def __init__(self, action_id, action):
self._id: str = action_id
self._type: str = action['type']
self._target: str = action['target']
self._condition : list[str] = action['condition']
# self._duration = action.get('duration', None)
# self._goto = action.get('goto', None)
# self._cycle = action.get('cycle', None)
@property
def id(self):
return self._id
@property
def type(self):
return self._type
@property
def target(self):
return self._target
@property
def condition(self):
return self._condition
def as_json(self):
return {
'id': self._id,
'type': self._type,
'target': self._target,
'condition': self._condition
}
-145
View File
@@ -1,145 +0,0 @@
from datetime import datetime
from time import time
class Condition():
def __init__(self, id, condition):
self._id = id
self._type = condition['type']
self._comparsion = condition['comparsion']
self._value = condition['value']
self._active = False
@property
def id(self):
return self._id
@property
def type(self):
return self._type
@property
def comparsion(self):
return self._comparsion
@property
def value(self):
return self._value
@id.setter
def id(self, new_id):
self._id = new_id
@type.setter
def type(self, new_type):
self._type = new_type
@comparsion.setter
def comparsion(self, new_comparsion):
self._comparsion = new_comparsion
@value.setter
def value(self, new_value):
self._value = new_value
def compareWith(self, operator, x, y) -> bool:
# print(operator, x, y)
cases = {
"equal": lambda a, b: a == b,
"bigger": lambda a, b: a >= b,
"smaller": lambda a, b: a <= b,
}
return cases[operator](x, y)
def match_or_not(self, **kwargs):
# print(self._type, self._comparsion, self._value)
return getattr(self, self.type)(**kwargs)
def absolute_time(self, **kwargs):
# now = int(time())
now = round(time(), 1)
time_condition = round(self.datetime_to_timestamp(self.str_to_datetime(self._value)))
return self.compareWith(self.comparsion, now, time_condition)
def after_project_run(self, **kwargs):
project_start_time = kwargs['project_start_time']
delay_time = kwargs['delay_time']
# time_diff = int(time() - project_start_time - delay_time)
time_diff = round(time() - project_start_time - delay_time, 1)
return self.compareWith(self.comparsion, time_diff, round(int(self._value), 1))
def after_task_run(self, **kwargs):
# print('relative_time_from_task', kwargs['task_start_time'],kwargs['delay_time'])
if len(kwargs['task_start_time']) == 0:
return False
task = kwargs['self_task']
running_task = kwargs['running_task']
task_start_time = kwargs['task_start_time'][-1]
delay_time = kwargs['delay_time']
time_diff = round(time() - task_start_time - delay_time, 1)
# print('real_diff', time() - task_start_time - delay_time)
# print(task.name, task.uuid, time_diff, round(int(self._value), 1), running_task.name, running_task.uuid)
# if round(int(self._value), 1) == 0 and time_diff == 0.1:
# return True
if task.uuid != running_task.uuid:
return False
return self.compareWith('bigger', time_diff, round(int(self._value), 1))
# return self.compareWith(self.comparsion, time_diff, round(int(self._value), 1))
def device(self, **kwargs):
print('device')
""" def previous_task_done(self, **kwargs):
running_task = kwargs['running_task']
if running_task.status == 2 and self._active == False:
self._active = True
return True
return False """
def previous_task_done(self, **kwargs):
running_task = kwargs['running_task']
self_task = kwargs['self_task']
# if running_task != None and self_task != None:
# print('\nprevious_task_done: ', running_task.status, ' ', self._active)
if running_task != self_task:
if running_task == None:
return True
if running_task.status == 2 and self._active == False:
# self._active = True
return True
return False
def until_button_trigger(self, **kwargs):
running_task = kwargs['running_task']
if running_task != None:
if running_task.button_trigger == True:
running_task.button_trigger = False
return True
return False
""" def cycle(self, **kwargs):
running_task = kwargs['running_task']
self_task = kwargs['self_task']
if running_task != self_task:
if running_task.status == 2 and self._active == False:
# self._active = True
return True
return False """
def str_to_datetime(self, time_str):
return datetime.strptime(time_str,'%Y-%m-%dT%H:%M')
def datetime_to_timestamp(self, date):
return datetime.timestamp(date)
def as_json(self):
return {
'id': self._id,
'type': self._type,
'comparsion': self._comparsion,
'value': self._value,
'active': self._active
}
-37
View File
@@ -1,37 +0,0 @@
class Instruction():
def __init__(self):
self._instruction_set = {
'set_file_name': {
'method': 'update_recording_file_name_info',
'arguments': ['file_name']
},
'set_parent': {
'method': 'update_parent_info',
'arguments': ['parent']
},
'set_parameter':{
'method': 'set_multi_parameters',
'arguments': ['parameter']
},
'call_instruction': {
'method': 'call_instruction',
'arguments': ['instruction']
},
}
self._start_instruction = list(map(lambda ins: self._instruction_set[ins] ,['set_file_name', 'set_parent', 'set_parameter', 'call_instruction']))
self._stop_instruction = list(map(lambda ins: self._instruction_set[ins] ,['call_instruction']))
self._idle_instruction = []
@property
def start(self) -> list:
return self._start_instruction
@property
def stop(self) -> list:
return self._stop_instruction
@property
def idle(self) -> list:
return self.idle_instruction
-392
View File
@@ -1,392 +0,0 @@
import sys
import json
import threading
import logging
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 biopro.db.base import Session
from biopro.db.collection import Collection
key_list = {
'deviceList': 'device',
}
class Project(threading.Thread):
def __init__(self, project, device_manager: DeviceManager, mqttThread = None, log_verbose = None, name="project"):
super(Project, self).__init__(name = name)
self._project = project
self._device_manager = device_manager
self._mqtt_thread = mqttThread
self._time_interval = 0.1
self._start_time = None
self._end_time = None
self._id = None
self._uuid = str(uuid4())
self._name = None
self._desc = None
self._device = None
self._complete_device = {}
self._status = 0
self._instruction_set = Instruction()
self._stop_flag = False
self._task_manager = None
self._cycle = []
self._count = 1 #流水號
self.log_verbose = log_verbose
self._logger = logging.getLogger('project')
self._logger.setLevel('DEBUG')
self._formatter = logging.Formatter('[%(asctime)s.%(msecs)03d] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
self._formatter_with_nothing = logging.Formatter('%(message)s')
self.setup_project(project)
self.setup_device(self._device)
# create log file handler
fh = logging.FileHandler(f'/home/pi/logger/project/{self.uuid}.log', mode="w")
fh.setFormatter(self._formatter)
self._logger.addHandler(fh)
default_name = 'admin'
default_parent = {"folder": [1]}
collection = Collection.find_collection(default_name, default_parent)
parent = {"folder": [collection.id]}
# create project folder
collection = Collection.create_collection(self.name, parent)
self.setup_collection(collection)
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'], project['cycle'])
elif key == 'uuid':
pass
else:
setattr(self, key, value)
def setup_device(self, device_list):
for device in device_list:
mac_address = device_list[device]['pair']
complete_device = self._device_manager.get_device(mac_address)
complete_device.occupied_by_project = self._uuid
self._complete_device[device] = complete_device
def setup_collection(self, collection):
self._task_manager.create_collection(collection)
@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 cycle(self) -> list:
return self._cycle
@cycle.setter
def cycle(self, new_cycle):
self._cycle = new_cycle
@property
def desc(self) -> str:
return self._desc
@desc.setter
def desc(self, new_desc):
self._desc = new_desc
@property
def status(self) -> str:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
@property
def device(self) -> list:
return self._device
@device.setter
def device(self, new_device):
self._device = new_device
@property
def task_list(self):
return self._task_manager.export_task_list
@property
def mqtt_thread(self):
return self._mqtt_thread
@property
def running_task(self):
return self._task_manager.running_task
def get_device_parameter_set(self, device_address):
parameter_set = self._task_manager.running_task.parameter_set
return next((x for x in parameter_set if parameter_set[x]['target'] == device_address), None)
def get_cycle(self, task):
for index, _cycle in enumerate(self.cycle):
if task.uuid in _cycle['range']:
return _cycle
def run(self):
# project status change running (1)
self._status = 1
# save start time
self._start_time = time()
# saving & broadcast message
self._logger.info('Project ' + self.name + ' start')
self.log_verbose('Project ' + self.name + ' start')
self.mqtt_thread.broadcast_command('project:' + self._name + ' start at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
while not self._stop_flag :
# check running task first
delay_time = 0
check_list = copy(self._task_manager.check_list)
for task in check_list:
if task != None:
now = time()
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,
self_task = task
)
for condition in match_condition_list:
match_action_list = task.get_match_action(condition.id)
print('match_action_list', match_action_list, match_action_list)
for action in match_action_list:
if action.type == 'cycle' and condition.type == 'previous_task_done':
self.mqtt_thread.broadcast_command('project:task ' + task.name + ' start at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
self._task_manager.set_running_task(task)
cycle_info = self.get_cycle(task)
if cycle_info['useID'] == False:
self._logger.info(f'---------- Cycle {task.name} Round {cycle_info["count"]} -----------')
if action.type == 'cycle' and condition.type == 'until_button_trigger':
self._task_manager.running_task.stop()
self._logger.info(f'---------- Cycle {cycle_info["id"]} Round {cycle_info["count"]} -----------')
if self._task_manager.running_task != None:
if self._task_manager.running_task.uuid == task.uuid and action.type == 'cycle' and condition.type == 'after_task_run':
self._task_manager.running_task.stop()
if action.type == 'start':
if task.status != 1:
self._task_manager.set_running_task(task)
self._logger.info(f'Task {str(self._task_manager.running_task.name)} start')
self.mqtt_thread.broadcast_command('project:task ' + task.name + ' start at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
# elif action.type == '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._count += 1
# if self.check_running_task_not_run() == True:
# self._task_manager.running_task.stop()
elif action.type == 'idle':
self._task_manager.running_task.stop()
# elif action.type == 'stop':
# self._task_manager.running_task.stop()
instruction_set = None
if action.target is not None:
device = self._complete_device[action.target]
if ((action.type == 'start' and device.status == 0) or (action.type == 'stop' and device.status == 1)):
self._logger.info(f'Device {device.mac_address_in_str} {action.type}')
self.log_verbose('Project' + self.name + 'Task ' + task.name + ' match_condition ' + condition.type + ' trigger_action ' + action.type + ' ' + str(action.target))
task_info = task.get_task_info(action)
instruction_set = getattr(self._instruction_set, action.type, None)
# 檔名修正
# device
task_info['file_name'] += '#' + device.mac_address_in_str[12:].upper()
# 流水號
task_info['file_name'] += '#' + str(self._count)
# 添加cycle資訊
if len(self.cycle) > 0:
index = self._task_manager.get_index_by_task(task)
# get list of cycle determine by index of task
filtered = list(filter(lambda info: self._task_manager.get_index_by_uuid(info['range'][0]) < index and self._task_manager.get_index_by_uuid(info['range'][1]) > index, self.cycle))
if len(filtered) == 0:
pass
elif len(filtered) == 1:
if filtered[0]['useID'] == True:
task_info['file_name'] += '#' + str(filtered[0]['id'])
else:
task_info['file_name'] += '#' + str(filtered[0]['name']) + '-' + str(filtered[0]['count'])
else:
# if multi cycle, then sort by range from task index to cycle start index, then loop concat filename
filtered.sort(key=lambda info: abs(self._task_manager.get_index_by_uuid(info['range'][0]) - index))
for cycle_info in filtered:
if cycle_info['useID'] == True:
task_info['file_name'] += '#' + str(cycle_info['id'])
else:
task_info['file_name'] += '#' + str(cycle_info['name']) + '-' + str(cycle_info['count'])
self.log_verbose('file name ' + task_info['file_name'])
if instruction_set != None and self.get_device_parameter_set(action.target) != None:
for instruction in instruction_set:
print('instruction 1', device, instruction, datetime.now())
args = list(map(lambda arg: task_info[arg], instruction['arguments']))
target=getattr(device, instruction['method'])(*args)
print('instruction 2', device, instruction, datetime.now())
if action.type == 'start':
self._count += 1
delay_time += (time() - now)
# check task not running then stop
if self.check_running_task_not_run() == True:
# self._logger.info(f'taskType {self._task_manager.running_task.name} {self._task_manager.running_task.type}')
if self._task_manager.running_task.type == '':
self._logger.info(f'Task {self._task_manager.running_task.name} 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._task_manager.running_task.stop()
# check project done then close project
if self.check_project_done() == True:
print('project stop at', datetime.now())
self.stop()
if self._time_interval - delay_time > 0:
sleep(self._time_interval - delay_time)
def pause(self):
# TODO
pass
def stop(self):
for device in self._complete_device:
self._complete_device[device].occupied_by_project = None
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._logger.info('Project ' + self.name + ' stop')
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):
for device in self._complete_device:
self._complete_device[device].occupied_by_project = None
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 set_content(self, content):
self.running_task.button_trigger = True
def set_cycle(self, index, content):
# change cycle name
for key in content:
self.cycle[index][key] = content[key]
# change task name
# self._task_manager.get_task_by_uuid(self.cycle[index]['range'][0]).name = content['name']
# self._task_manager.get_task_by_uuid(self.cycle[index]['range'][1]).name = content['name']
def check_running_task_not_run(self):
# if no running task
if self._task_manager.running_task == None:
return False
# if running is cycle then no need to stop by device
if self._task_manager.running_task.type == 'cycle':
return False
for key in self._task_manager.running_task.action:
# print('action key', key, self._task_manager.running_task, self._task_manager.running_task.action[key])
if len(self._task_manager.running_task.parameter_set) == 0 and self._task_manager.running_task.action[key]['type'] == 'stop':
return False
if len(self._task_manager.running_task.parameter_set) == 0 and self._task_manager.running_task.action[key]['type'] == 'idle':
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):
running_task = None
if self._task_manager.running_task is not None:
running_task = self._task_manager.running_task.as_json()
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': running_task,
'cycle': self._cycle,
'count': self._count,
}
return data
def info_pass_data_server(self):
if self._task_manager.running_task is not None:
running_task = self._task_manager.running_task.as_json()
data = {
'project': self._uuid,
'task': running_task,
'cycle': self._cycle,
'serial_number': self._count,
}
return data
-48
View File
@@ -1,48 +0,0 @@
from io import StringIO
from json import loads as json_parse, dumps as _json_stringify
from typing import Dict, Optional, Any
import paho.mqtt.client as mqtt
from .project import Project
from biopro.device.manager import DeviceManager
def json_stringify(o: Any) -> str:
return _json_stringify(o, separators=(',', ':'))
class ProjectManager():
def __init__(self, device_manager: DeviceManager, mqtt_thread=None, log_verbose = None) :
self._project_list = []
self._mqtt_thread = mqtt_thread
self._device_manager = device_manager
self.log_verbose = log_verbose
def create(self, project):
new_project = Project(project, self._device_manager, self._mqtt_thread, self.log_verbose)
self._project_list.append(new_project)
return new_project
def remove(self, index):
self._project_list[index].stop()
del self._project_list[index]
def get(self, project_uuid = None):
if project_uuid == None:
return self._project_list
else:
for project in self._project_list:
if project.uuid == project_uuid:
return project
def run_project(self, project):
project.start()
def stop_project(self, project):
_project = self.get(project)
_project.stop()
def set_project(self, project_uuid, content):
_project = self.get(project_uuid)
_project.set_content(content)
def set_project_cycle(self, project_uuid, index, content):
_project = self.get(project_uuid)
_project.set_cycle(index, content)
-312
View File
@@ -1,312 +0,0 @@
from typing import List
from .condition import Condition
from .action import Action
from time import time
from datetime import datetime
key_list = {
'deviceList': 'device',
'parameterSet': 'parameter_set'
}
class Task:
def __init__(self, task):
self._id = None
self._uuid = None
self._type = None
self._name = None
self._parent = None
self._cycle = None
self._cycle_count = 0
self._cycle_next = None
self._cycle_next_uuid = None
self._device = None
self._event = None
self._trigger = None
self._parameter_set = None
self._condition = None
self._action = None
# -1: init 0: start(idle), 1: run, 2: close(idle)
self._status = 0
self._next = None
# action & condition info
self._condition_list: List[Condition] = []
self._action_list: List[Action] = []
self._instruction_list = []
self._record_list = []
# time info
self._start_time = []
self._idle_time = []
self._end_time = []
self._period = None
# trigger info
self._button_trigger = False
self.load_task(task)
def load_task(self, task) -> None:
# update task attribute
for key, value in task.items():
if key in key_list.keys():
key = key_list[key]
if key == 'condition':
self.update_condition_list(value)
if key == 'action':
self.update_action_list(value)
# print(key, value)
setattr(self, key, value)
def update_condition_list(self, condition_dict):
self._condition_list.clear()
for key, condition in condition_dict.items():
self._condition_list.append(Condition(key, condition))
def update_action_list(self, action_dict):
self._action_list.clear()
for key, action in action_dict.items():
self._action_list.append(Action(key, action))
@property
def id(self) -> str:
return self._id
@id.setter
def id(self, new_id):
self._id = new_id
@property
def uuid(self) -> str:
return self._uuid
@uuid.setter
def uuid(self, new_uuid):
self._uuid = new_uuid
@property
def type(self):
return self._type
@type.setter
def type(self, new_type):
self._type = new_type
@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
# TODO: not sure needed?
@property
def cycle_count(self) -> int:
return self._cycle_count
@cycle_count.setter
def cycle_count(self, new_cycle_count):
self._cycle_count = new_cycle_count
@property
def cycle_next(self) -> str:
return self._cycle_next
@cycle_next.setter
def cycle_next(self, new_cycle_next):
self._cycle_next = new_cycle_next
@property
def cycle_next_uuid(self) -> str:
return self._cycle_next_uuid
@cycle_next_uuid.setter
def cycle_next_uuid(self, new_cycle_next_uuid):
self._cycle_next_uuid = new_cycle_next_uuid
@property
def device(self) -> dict:
return self._device
@device.setter
def device(self, new_device):
self._device = new_device
@property
def event(self) -> list:
return self._event
@event.setter
def event(self, new_event):
self._event = new_event
@property
def trigger(self) -> list:
return self._trigger
@trigger.setter
def trigger(self, new_trigger):
self._trigger = new_trigger
@property
def parameter_set(self) -> list:
return self._parameter_set
@parameter_set.setter
def parameter_set(self, new_parameter_set):
self._parameter_set = new_parameter_set
@property
def condition(self) -> list:
return self._condition
@condition.setter
def condition(self, new_condition):
self._condition = new_condition
@property
def action(self) -> list:
return self._action
@action.setter
def action(self, new_action):
self._action = new_action
@property
def status(self) -> str:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
@property
def next(self) -> str:
return self._next
@next.setter
def next(self, new_next):
self._next = new_next
@property
def instruction_list(self) -> List:
return self._instruction_list
@property
def start_time(self) -> List:
return self._start_time
@property
def end_time(self) -> List:
return self._end_time
@property
def button_trigger(self) -> List:
return self._button_trigger
@button_trigger.setter
def button_trigger(self, button_trigger):
self._button_trigger = button_trigger
def new_start_time(self):
self._start_time.append(time())
def new_end_time(self):
self._end_time.append(time())
def get_parameter_set_by_device(self, device):
return [parameter_set for parameter_set in self.parameter_set.values() if device == parameter_set['target']]
def get_parameter(self, name_or_value, device):
name_list = []
value_list = []
for parameter in self.get_parameter_set_by_device(device):
for key, value in parameter.items():
if key != 'target':
name_list.append(key)
value_list.append(str(value))
return '|'.join(name_list) if name_or_value == 'name' else '|'.join(value_list)
def run(self):
print(self._name, 'run at', datetime.now())
self._status = 1
self._start_time.append(time())
def start(self):
self.status = 1
def stop(self):
self.status = 2
def reset(self):
self.status = -1
# self.status = 0
def get_match_action_list(self, match_condition_list):
return map(lambda condition: [x for x in self._action_list if condition.id in x.get_condition_list()], match_condition_list)
def get_match_action(self, condition_id):
return [action for action in self._action_list if condition_id in action.condition]
def do_action(self, action):
for instruction in action.get_instruction_list():
self._instruction_list.append(instruction)
def check_condition(self, **kwargs):
kwargs['task_start_time'] = self._start_time
return [condition for condition in self._condition_list if condition.match_or_not(**kwargs)]
def get_task_info(self, action):
_type = action.type
if _type == 'stop':
_type = 'interrupt'
return {
'device': action.target,
'parameter': self.get_parameter_set_by_device(action.target),
'file_name': self._name,
'parent': self.parent,
'instruction': _type
}
def as_json(self):
return {
'id': self.id,
'name': self.name,
'parent': self.parent,
'type': self.type,
'uuid': self.uuid,
'cycle': self.cycle,
'cycle_count': self.cycle_count,
'cycle_next': self.cycle_next,
'cycle_next_uuid': self.cycle_next_uuid,
'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,
}
-181
View File
@@ -1,181 +0,0 @@
from json import loads as json_parse, dumps as _json_stringify
from socket import if_nametoindex
from typing import Dict, List, Optional, Any
from xml.dom.expatbuilder import parseString
import paho.mqtt.client as mqtt
from biopro.text import *
from .task import Task
from biopro.db.collection import Collection
_RUNTIME_COMPILE = False
def json_stringify(o: Any) -> str:
return _json_stringify(o, separators=(',', ':'))
class TaskManager():
def __init__(self, task_list, cycle_list):
self._task_list = []
self._next_task = []
self._prev_task = None
self._running_task = None
self._cycle_list = cycle_list
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
def get_task_by_index(self, index):
return self._task_list[index]
def get_task_by_uuid(self, uuid):
return next((x for x in self.task_list if x.uuid == uuid), None)
def get_index_by_task(self, task):
return next((i for i, x in enumerate(self.task_list) if x.uuid == task.uuid), None)
def get_index_by_uuid(self, uuid):
return next((i for i, x in enumerate(self.task_list) if x.uuid == uuid), None)
def check_task_is_cycle_start(self, task):
for index, cycle in enumerate(self._cycle_list):
if cycle['range'][0] == task.uuid:
return index
def check_task_is_cycle_end(self, task):
for index, cycle in enumerate(self._cycle_list):
if cycle['range'][1] == task.uuid:
return index
def check_task_in_cycle(self, task_index):
for index, cycle in enumerate(self._cycle_list):
if self.get_index_by_uuid(cycle['range'][0]) <= task_index:
if self.get_index_by_uuid(cycle['range'][1]) >= task_index:
return True
return False
@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)
# original version
""" 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 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 task
self._prev_task = self._running_task
# clear next task list
self._next_task.clear()
# set task to running_task
self._running_task = task
self._running_task.run()
# if previous task is still running, then need to close
if self._prev_task != None:
if self._prev_task.status == 1:
self._prev_task.stop()
# if reach cycle end, count < max, then go to cycle start
cycle_start_index = self.check_task_is_cycle_start(task)
cycle_index = self.check_task_is_cycle_end(task)
if cycle_index is None:
# print('next', self._running_task.name, self._running_task.next)
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:
_task.reset()
self._next_task.append(_task)
# print('self._next_task', self._next_task)
else:
# print('cycle', self._cycle_list[cycle_index])
cycle = self._cycle_list[cycle_index]
if int(cycle['count']) < int(cycle['max']):
self._next_task.append(self.get_task_by_uuid(cycle['range'][0]))
# cycle['count'] += 1
else:
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:
_task.reset()
self._next_task.append(_task)
# cycle['count'] = 1
if cycle_start_index != None:
cycle = self._cycle_list[cycle_start_index]
if int(cycle['count']) < int(cycle['max']):
cycle['count'] += 1
else:
cycle['count'] = 1
except RuntimeError as e:
print(e)
def get_task(self, task_id):
return self._task_list[task_id]
def create_collection(self, parent):
for index, task in enumerate(self._task_list):
if self.check_task_in_cycle(index) == True:
if task.type == '':
collection = Collection.create_collection(task.name, {"folder": [parent.id]})
task.parent = {"folder": [collection.id]}
else:
task.parent = {"folder": [parent.id]}
+71 -120
View File
@@ -8,7 +8,6 @@ import gc
from biopro.util.json import JSON from biopro.util.json import JSON
from biopro.util.stack import print_exception from biopro.util.stack import print_exception
from biopro.util.logger import calculate_time
from .data import RecordingData from .data import RecordingData
from .loader import * from .loader import *
@@ -20,8 +19,6 @@ from statistics import mean
import random import random
# from numba import jit # from numba import jit
from copy import copy
def json_stringify(o) -> str: def json_stringify(o) -> str:
return _json_stringify(o, separators=(',', ':')) return _json_stringify(o, separators=(',', ':'))
@@ -118,7 +115,7 @@ class RecordingMetaFile(JsonSerialize):
'_dirty', '_last_modify_time', '_file_size_cache', '_dirty', '_last_modify_time', '_file_size_cache',
'_recording_file_ch', '_recording_sub_file', '_size', '_recording_file_ch', '_recording_sub_file', '_size',
'_parameter', '_parent', '_recording_file_name', '_recording_sub_mini', '_recording_mini_ch', '_parameter', '_parent', '_recording_file_name', '_recording_sub_mini', '_recording_mini_ch',
'_raw_serial_number', '_mini_serial_number', '_device_id', '_project_id') '_raw_serial_number', '_mini_serial_number', '_device_id')
def __init__(self, filepath: Union[str, Path], data_format: bytes = None, id_db = 0, database = None): def __init__(self, filepath: Union[str, Path], data_format: bytes = None, id_db = 0, database = None):
if isinstance(filepath, str): if isinstance(filepath, str):
@@ -153,7 +150,6 @@ class RecordingMetaFile(JsonSerialize):
self._parent = '' self._parent = ''
self._recording_file_name = '' self._recording_file_name = ''
self._device_id = -1 self._device_id = -1
self._project_id = None
# recording parameter # recording parameter
self._configuration = None self._configuration = None
@@ -298,7 +294,6 @@ class RecordingMetaFile(JsonSerialize):
'uuid': str(self._file_uuid), 'uuid': str(self._file_uuid),
'data_format': 'rec', 'data_format': 'rec',
'device': self._device, 'device': self._device,
'project': self._project_id,
} }
if len(self._parent) > 0: if len(self._parent) > 0:
@@ -375,45 +370,6 @@ class RecordingMetaFile(JsonSerialize):
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db]) self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db]) # database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None return None
def update_subfile_time_size(self, database = None):
meta_data = {
'channels': str(self._channel_mask.channels()),
'size': str(self._size),
'time_duration': str(self._last_time),
'uuid': str(self._file_uuid),
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
def update_subfile_raw(self, database = None):
meta_data = {
'raw_data': self._recording_sub_file,
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
def update_subfile_mini(self, database = None):
meta_data = {
'mini_data': self._recording_sub_mini,
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
@property @property
def filename(self) -> str: def filename(self) -> str:
@@ -464,7 +420,7 @@ class RecordingMetaFile(JsonSerialize):
def parameter(self) -> str: def parameter(self) -> str:
return self._parameter return self._parameter
@parameter.setter @device.setter
def parameter(self, parameter): def parameter(self, parameter):
self._parameter = parameter self._parameter = parameter
return None return None
@@ -473,7 +429,7 @@ class RecordingMetaFile(JsonSerialize):
def parent(self) -> str: def parent(self) -> str:
return self._parent return self._parent
@parent.setter @device.setter
def parent(self, parent): def parent(self, parent):
self._parent = parent self._parent = parent
@@ -481,7 +437,7 @@ class RecordingMetaFile(JsonSerialize):
def recording_file_name(self) -> str: def recording_file_name(self) -> str:
return self._recording_file_name return self._recording_file_name
@recording_file_name.setter @device.setter
def recording_file_name(self, recording_file_name): def recording_file_name(self, recording_file_name):
self._recording_file_name = recording_file_name self._recording_file_name = recording_file_name
@@ -494,15 +450,6 @@ class RecordingMetaFile(JsonSerialize):
self._configuration = conf self._configuration = conf
self._dirty = True self._dirty = True
return None return None
@property
def project_id(self) -> int:
return self._project_id
@project_id.setter
def project_id(self, project: int):
self._project_id = project
return None
@property @property
def channels(self) -> List[int]: def channels(self) -> List[int]:
@@ -570,6 +517,11 @@ class RecordingMetaFile(JsonSerialize):
'start_time': str(_start_time), 'start_time': str(_start_time),
} }
# if database is not None:
# database.data_raw_create(self, f, raw_data, _channel)
# self._recording_file_ch[_channel]
return f, raw_data return f, raw_data
def new_recording_mini(self, _channel: int, _start_time, scale, database = None) -> 'RecordingMini': def new_recording_mini(self, _channel: int, _start_time, scale, database = None) -> 'RecordingMini':
@@ -615,6 +567,9 @@ class RecordingMetaFile(JsonSerialize):
'start_time': str(_start_time), 'start_time': str(_start_time),
} }
# if database is not None:
# database.data_mini_create(self, f, mini_data, _channel, scale)
return f, mini_data return f, mini_data
def clear_recording_file(self): def clear_recording_file(self):
@@ -673,6 +628,15 @@ class RecordingMetaFile(JsonSerialize):
'device': self._device, 'device': self._device,
} }
# d = self._device
# if d is not None:
# ret['device'] = {
# 'device_name': d.device_name,
# 'device_address': list(d.mac_address),
# 'serial_number': d.serial_number.as_json(),
# }
return ret return ret
@@ -828,10 +792,14 @@ class RecordingFile:
'end_time': str(_end_time), 'end_time': str(_end_time),
'size': str(self._size) 'size': str(self._size)
} }
# if self._meta_file._id_db == 0:
# _path = str(self._meta_file.filepath).replace('/', '^')
# self._database.put_queue(['data_meta_id_get_by_path', _path])
# # self._meta_file._id_db = self._database.put_queue(['data_meta_id_get_by_path', _path])
if self._meta_file._id_db != 0: if self._meta_file._id_db != 0:
_data['parent'] = self._meta_file._id_db _data['parent'] = self._meta_file._id_db
self._database.put_queue(['data_raw_update', self._id_db, self._channel, _data]) self._database.put_queue(['data_raw_update', self._id_db, self._channel, _data])
# self._database.put_queue(['data_raw_update', self._id_db, self._channel, _data])
self._status = False self._status = False
return None return None
@@ -997,7 +965,10 @@ class RecordingMini:
'end_time': str(_end_time), 'end_time': str(_end_time),
'size': str(self._size) 'size': str(self._size)
} }
# if self._meta_file._id_db == 0:
# _path = str(self._meta_file.filepath).replace('/', '^')
# self._database.put_queue(['data_meta_id_get_by_path', _path])
# # self._meta_file._id_db = self._database.put_queue(['data_meta_id_get_by_path', _path])
if self._meta_file._id_db != 0: if self._meta_file._id_db != 0:
_data['parent'] = self._meta_file._id_db _data['parent'] = self._meta_file._id_db
self._database.put_queue(['data_mini_update', self._id_db, self._channel, _data]) self._database.put_queue(['data_mini_update', self._id_db, self._channel, _data])
@@ -1025,8 +996,7 @@ class RecordingFileWriter:
'_data_value_ch', '_close', '_data_mini_ch', '_data_value_ch', '_close', '_data_mini_ch',
'_mini_scale_list', '_time_real_time', '_data_rl', '_data_db', '_mini_scale_list', '_time_real_time', '_data_rl', '_data_db',
'_raw_save', '_mini_save', '_data_time_ch', '_data_value_ch_for_rl', '_raw_save', '_mini_save', '_data_time_ch', '_data_value_ch_for_rl',
'_data_time_ch_for_rl', '_device_id', '_send_data', '_data_mqtt_ch', '_id_db_save', '_raw_create_not_done', '_data_time_ch_for_rl', '_device_id', '_send_data', '_data_mqtt_ch')
'_mini_create_not_done')
def __init__(self, meta: RecordingMetaFile, device_id, database = None): def __init__(self, meta: RecordingMetaFile, device_id, database = None):
self._meta = meta self._meta = meta
@@ -1047,9 +1017,7 @@ class RecordingFileWriter:
self._raw_save = { self._raw_save = {
'id': {}, 'id': {},
'data': {}, 'data': {}
'end_time': {},
'size': {}
} }
self._mini_save = { self._mini_save = {
'10': { '10': {
@@ -1084,7 +1052,6 @@ class RecordingFileWriter:
self._data_value_ch_for_rl = {} self._data_value_ch_for_rl = {}
self._data_time_ch_for_rl = {} self._data_time_ch_for_rl = {}
self._data_time_ch = {} self._data_time_ch = {}
self._id_db_save = {}
# mini data # mini data
self._data_mini_ch = {} self._data_mini_ch = {}
@@ -1101,15 +1068,12 @@ class RecordingFileWriter:
# splitting # splitting
self.splitting_threshold_time = 30 * 60 * 1000 # one minute self.splitting_threshold_time = 30 * 60 * 1000 # one minute
self.splitting_threshold_size = 16 * 1024 # 16 * 16KB self.splitting_threshold_size = 16 * 16 * 1024 # 16 * 16KB
self._writer_batch_size = 8192 self._writer_batch_size = 8192
self._splitting_size = None self._splitting_size = None
self._raw_create_not_done = True
self._mini_create_not_done = True
@property @property
def meta_file(self) -> RecordingMetaFile: def meta_file(self) -> RecordingMetaFile:
@@ -1142,17 +1106,14 @@ class RecordingFileWriter:
self._splitting_size = 0 self._splitting_size = 0
return None return None
def close(self, mqtt_thread): def close(self):
# if self._recording_file is not None:
# self._recording_file.close()
# self._recording_file = None
print('close1')
self._close = True self._close = True
if len(self._recording_file_dict) > 0: if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys(): for ch in self._data_db.keys():
if len(self._data_rl[ch]) > 0:
self._data_rl[ch].append(str(int(self._time_now)))
mes = ' '.join(self._data_rl[ch])
mqtt_thread[ch].on_message(mes)
self._data_rl[ch].clear()
self._send_data[ch] = False
if self._recording_file_dict[ch]._status: if self._recording_file_dict[ch]._status:
_data = ' '.join(self._data_db[ch]) _data = ' '.join(self._data_db[ch])
self._raw_save['data'][ch] = _data self._raw_save['data'][ch] = _data
@@ -1162,7 +1123,7 @@ class RecordingFileWriter:
self._meta._size += self._recording_file_dict[ch]._size self._meta._size += self._recording_file_dict[ch]._size
# self._data_db.clear() # self._data_db.clear()
if self._database is not None: if self._database is not None:
self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data'], self._id_db_save]) self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
# self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']]) # self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
self._recording_file_dict.clear() self._recording_file_dict.clear()
for scale in self._mini_scale_list: for scale in self._mini_scale_list:
@@ -1328,8 +1289,7 @@ class RecordingFileWriter:
self._data_db[c].append(str(v)) self._data_db[c].append(str(v))
self._time_now = int(t) self._time_now = int(t)
return return
# @calculate_time(1)
def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread) -> int: def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread) -> int:
# check size # check size
ths = self.splitting_threshold_size ths = self.splitting_threshold_size
@@ -1388,54 +1348,42 @@ class RecordingFileWriter:
self.get_data_iter(d, mqtt_thread) self.get_data_iter(d, mqtt_thread)
data_save = False
mini_save = False
if len(self._recording_file_dict) > 0: if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys(): for ch in self._data_db.keys():
if self._time_now - self._time_real_time[ch] > 1000000: if self._time_now - self._time_real_time[ch] > 1000000:
self._send_data[ch] = True self._send_data[ch] = True
self._time_real_time[ch] = self._time_now self._time_real_time[ch] = self._time_now
# # if self._time_now - self._time_real_time[ch] > 500000:
for ch in self._recording_file_dict: # self._data_rl[ch].append(str(self._time_now))
if self._recording_file_dict[ch]._id_db == 0: # mes = ' '.join(self._data_rl[ch])
return None # # print(mes)
# # if mqtt_thread is not None:
for ch in self._data_db.keys(): # # mqtt_thread.publish('data_server/device_data_stream/' + str(self._device_id) + '/' + str(ch), mes)
for scale in self._mini_scale_list: # # mqtt_queue.put([self._device_id, ch, mes])
if self._recording_mini_dict[str(scale)][ch]._id_db == 0: # mqtt_thread[ch].on_message(mes)
return None # self._data_rl[ch].clear()
# # self._time_real_time[ch] = self._time_now
if self._raw_create_not_done :
self._raw_create_not_done = False
self._meta.update_subfile_raw(database = self._database)
if self._mini_create_not_done :
self._mini_create_not_done = False
self._meta.update_subfile_mini(database = self._database)
data_save = False
mini_save = False
if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys():
if self._time_now - self._time[ch] > 5000000: if self._time_now - self._time[ch] > 5000000:
if self._recording_file_dict[ch]._status: if self._recording_file_dict[ch]._status:
_data = ' '.join(self._data_db[ch]) _data = ' '.join(self._data_db[ch])
write_sz = self._recording_file_dict[ch].write(_data, self._channel_list) write_sz = self._recording_file_dict[ch].write(_data, self._channel_list)
sz = sz + write_sz
self._raw_save['data'][ch] = _data self._raw_save['data'][ch] = _data
self._raw_save['id'][ch] = self._recording_file_dict[ch]._id_db self._raw_save['id'][ch] = self._recording_file_dict[ch]._id_db
self._raw_save['end_time'][ch] = self._time_now data_save = True
self._raw_save['size'][ch] = self._recording_file_dict[ch]._size
self._data_db[ch].clear() self._data_db[ch].clear()
self._time[ch] = self._time_now self._time[ch] = self._time_now
self._splitting_size += sz
self._meta._last_time = self._time_now self._meta._last_time = self._time_now
self._splitting_size += write_sz self._recording_file_dict[ch].update_endtime(self._time_now)
data_save = True
if len(self._data_mini_ch[ch]['1000']['mean']) >= 10: if len(self._data_mini_ch[ch]['1000']['mean']) >= 10:
mini_save = True mini_save = True
for scale in self._mini_scale_list: for scale in self._mini_scale_list:
str_mean = [str(int) for int in self._data_mini_ch[ch][str(scale)]['mean']]
data_mean = str(self._data_mini_ch[ch][str(scale)]['start_time']) + ' ' + ' '.join(str_mean) + '"***"'
self._mini_save[str(scale)]['id'][ch] = self._recording_mini_dict[str(scale)][ch]._id_db self._mini_save[str(scale)]['id'][ch] = self._recording_mini_dict[str(scale)][ch]._id_db
self._mini_save[str(scale)]['start_time'][ch] = str(self._data_mini_ch[ch][str(scale)]['start_time']) self._mini_save[str(scale)]['start_time'][ch] = str(self._data_mini_ch[ch][str(scale)]['start_time'])
self._mini_save[str(scale)]['data_mean'][ch] = data_mean self._mini_save[str(scale)]['data_mean'][ch] = self._data_mini_ch[ch][str(scale)]['mean'].copy()
# self._mini_save[str(scale)]['data_random'][ch] = self._data_mini_ch[ch][str(scale)]['random'].copy() # self._mini_save[str(scale)]['data_random'][ch] = self._data_mini_ch[ch][str(scale)]['random'].copy()
# self._mini_save[str(scale)]['data_bar'][ch] = self._data_mini_ch[ch][str(scale)]['bar'].copy() # self._mini_save[str(scale)]['data_bar'][ch] = self._data_mini_ch[ch][str(scale)]['bar'].copy()
self._recording_mini_dict[str(scale)][ch].write(self._mini_save[str(scale)]['data_mean'][ch]) self._recording_mini_dict[str(scale)][ch].write(self._mini_save[str(scale)]['data_mean'][ch])
@@ -1446,25 +1394,26 @@ class RecordingFileWriter:
if data_save is True: if data_save is True:
if self._database is not None: if self._database is not None:
recording_input = ['data_raw_recording_new', copy(self._raw_save['id']), copy(self._channel_list), copy(self._raw_save['data']), copy(self._raw_save['end_time']), copy(self._raw_save['size'])] self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
self._database.put_queue(recording_input) self._meta.update_subfile(database = self._database)
self._meta.update_subfile_time_size(database = self._database)
if mini_save is True: if mini_save is True:
if self._database is not None: if self._database is not None:
for scale in self._mini_scale_list: for scale in self._mini_scale_list:
self._database.put_queue(['data_mini_recording_new', copy(self._mini_save[str(scale)]['id']), self._channel_list, copy(self._mini_save[str(scale)]['data_mean'])]) self._database.put_queue(['data_mini_recording', self._mini_save[str(scale)]['id'], self._channel_list, self._mini_save[str(scale)]['start_time'], self._mini_save[str(scale)]['data_mean']])
self._meta.update_subfile_time_size(database = self._database) self._meta.update_subfile(database = self._database)
del data del data
return None return None
def _switch_recording_file(self): def _switch_recording_file(self):
self._raw_create_not_done = True # if self._recording_file is not None:
# self._recording_file.close()
if len(self._recording_file_dict) > 0: if len(self._recording_file_dict) > 0:
for ch in self._recording_file_dict.keys(): for ch in self._recording_file_dict.keys():
self._id_db_save[ch] = self._recording_file_dict[ch]._id_db
self._recording_file_dict[ch].close(self._time_now) self._recording_file_dict[ch].close(self._time_now)
self._meta._size += self._recording_file_dict[ch]._size self._meta._size += self._recording_file_dict[ch]._size
@@ -1480,16 +1429,16 @@ class RecordingFileWriter:
if self._database is not None: if self._database is not None:
self._database.put_queue(['data_raw_create', raw_data_dict, self._channel_list, self._device_id]) self._database.put_queue(['data_raw_create', raw_data_dict, self._channel_list, self._device_id])
# self._database.put_queue(['data_raw_create', self._meta, self._recording_file_dict, raw_data_dict, self._channel_list])
self._meta._last_time = self._time_now self._meta._last_time = self._time_now
# self._meta.update_subfile(database = self._database) self._meta.update_subfile(database = self._database)
self._splitting_size = 0 self._splitting_size = 0
return None return None
def _switch_recording_mini(self, scale): def _switch_recording_mini(self, scale):
self._mini_create_not_done = True
if len(self._recording_mini_dict[str(scale)]) > 0: if len(self._recording_mini_dict[str(scale)]) > 0:
for ch in self._recording_mini_dict[str(scale)].keys(): for ch in self._recording_mini_dict[str(scale)].keys():
@@ -1507,12 +1456,14 @@ class RecordingFileWriter:
if self._database is not None: if self._database is not None:
self._database.put_queue(['data_mini_create', mini_data_dict, self._channel_list, scale, self._device_id]) self._database.put_queue(['data_mini_create', mini_data_dict, self._channel_list, scale, self._device_id])
# self._database.put_queue(['data_mini_create', self._meta, self._recording_mini_dict[str(scale)], mini_data_dict, self._channel_list, scale])
self._meta._last_time = self._time_now self._meta._last_time = self._time_now
# self._meta.update_subfile(database = self._database) self._meta.update_subfile(database = self._database)
return None return None
def update_meta_id(self, _id): def update_meta_id(self, _id):
# print('update meta')
self._meta._id_db = _id self._meta._id_db = _id
return None return None
+17 -3
View File
@@ -9,9 +9,23 @@ _jwt = None
def controller_device_id() -> str: def controller_device_id() -> str:
### device ID ### device ID
f = open("/sys/class/net/eth0/address", "r") return 'b8:27:eb:52:ea:97'
mac = f.read()[:-1] ret = None
return mac try:
with open('/etc/BPS/device_id') as f:
for line in f:
if line.startswith('#'):
continue
ret = line.strip()
break
except FileNotFoundError:
pass
if ret is None or len(ret) == 0:
return str(uuid.uuid1())
else:
return ret
def get_api_jwt() -> str: def get_api_jwt() -> str:
global _jwt global _jwt
-3
View File
@@ -1335,6 +1335,3 @@ def to_device_info(content: Union[str, ADDRESS, JSON_OBJECT]) -> DeviceResponseI
return DeviceResponseInfo(None, return DeviceResponseInfo(None,
serial_number=serial, serial_number=serial,
mac_address=address) mac_address=address)
def run_project(self, project) -> bool:
return False
+55 -87
View File
@@ -4,6 +4,7 @@ from time import time, sleep
from typing import Iterable from typing import Iterable
from datetime import datetime from datetime import datetime
import gc import gc
import base64 import base64
from biopro.data import * from biopro.data import *
@@ -26,6 +27,7 @@ from .mqtt import MqttDataMessageHandler, MqttThread
from .database import DataBaseProcess from .database import DataBaseProcess
from .recording import RecordingProcess from .recording import RecordingProcess
from multiprocessing import Queue from multiprocessing import Queue
import queue
from json import loads as json_parse, dumps as _json_stringify from json import loads as json_parse, dumps as _json_stringify
import json import json
@@ -63,7 +65,7 @@ class DataServer(SocketServer, DataAPI):
""" """
global _FLAG_DATA_LOST_STATS_ global _FLAG_DATA_LOST_STATS_
super().__init__(DataAPI, 'DataServer', self.ADDR, socket_listen=20) super().__init__(DataAPI, 'DataServer', self.ADDR, socket_listen=3)
data_options = DataServerOptions.get_options(*options) data_options = DataServerOptions.get_options(*options)
@@ -102,9 +104,18 @@ class DataServer(SocketServer, DataAPI):
self.log_verbose, self.log_verbose,
'MQTT-data-server') 'MQTT-data-server')
# api socket # api socket
self._queue_spi = queue.Queue()
# self._queue_spi_dict = {}
self._queue_ds_dict = {} self._queue_ds_dict = {}
self._queue_db = Queue() self._queue_db = Queue()
self._queue_msg = Queue() self._queue_db_error = Queue()
# self._queue_ds = Queue()
# if len(self._queue_spi_dict) == 0:
# self._queue_spi_dict[4] = Queue()
# self._queue_spi_dict[5] = Queue()
# self._queue_spi_dict[6] = Queue()
# self._queue_spi_dict[7] = Queue()
if len(self._queue_ds_dict) == 0: if len(self._queue_ds_dict) == 0:
self._queue_ds_dict[4] = Queue() self._queue_ds_dict[4] = Queue()
@@ -112,12 +123,9 @@ class DataServer(SocketServer, DataAPI):
self._queue_ds_dict[6] = Queue() self._queue_ds_dict[6] = Queue()
self._queue_ds_dict[7] = Queue() self._queue_ds_dict[7] = Queue()
self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_msg) self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_db_error)
self.database_process.start() self.database_process.start()
# self._no_data_total_duration = 0
# self._recv_memory_cost_time = 0
@logging_info @logging_info
def setup(self): def setup(self):
self._available_channel.clear() self._available_channel.clear()
@@ -138,6 +146,7 @@ class DataServer(SocketServer, DataAPI):
try: try:
self._stop_runtime() self._stop_runtime()
self.stop_sync() self.stop_sync()
self._queue_spi.queue.clear()
finally: finally:
self._configurations.clear() self._configurations.clear()
@@ -176,6 +185,9 @@ class DataServer(SocketServer, DataAPI):
self._configurations.clear() self._configurations.clear()
with CacheClient() as client:
client.clear()
# reopen spi, which has close at close() # reopen spi, which has close at close()
if spi_mode is None: if spi_mode is None:
spi_mode = self._spi_mode spi_mode = self._spi_mode
@@ -315,8 +327,7 @@ class DataServer(SocketServer, DataAPI):
def update_device_configuration(self, def update_device_configuration(self,
device: str, device: str,
meta_file: Union[str, Path, RecordingMetaFile], meta_file: Union[str, Path, RecordingMetaFile],
data_format: Union[AnyStr, DataDecodeFormat], data_format: Union[AnyStr, DataDecodeFormat]):
project_info: str):
# meta file # meta file
device_id = -1 device_id = -1
@@ -344,8 +355,6 @@ class DataServer(SocketServer, DataAPI):
_parent = json_stringify(_device['parent']) _parent = json_stringify(_device['parent'])
_recording_file_name = _device['recording_file_name'] _recording_file_name = _device['recording_file_name']
mac_address = json.loads(device)['device_address'] mac_address = json.loads(device)['device_address']
device_id = _device['device_id']
del _device['configuration'] del _device['configuration']
del _device['parent'] del _device['parent']
del _device['recording_file_name'] del _device['recording_file_name']
@@ -355,22 +364,13 @@ class DataServer(SocketServer, DataAPI):
# meta_file.parameter = _parameter # meta_file.parameter = _parameter
# meta_file.parent = _parent # meta_file.parent = _parent
# meta_file.recording_file_name = _recording_file_name # meta_file.recording_file_name = _recording_file_name
device_id = int(json.loads(device)['device_id'])
if device_id < 0: if device_id < 0:
raise ValueError('illegal device ID : ' + str(device_id)) raise ValueError('illegal device ID : ' + str(device_id))
self.log_verbose('device ID', device_id) self.log_verbose('device ID', device_id)
# project binding meta file
# project_id = None
# _project = None
# if project_info != None:
# _project = json.loads(project_info)
# self.database_process.put_queue(['project_insert', device_id, _project])
# result = self._queue_ds_dict[int(device_id)].get()
# if result[0] == 'project_id':
project_id = project_info
# while len(self._configurations) <= device_id: # while len(self._configurations) <= device_id:
# self._configurationsappend(None) # self._configurationsappend(None)
@@ -390,6 +390,8 @@ class DataServer(SocketServer, DataAPI):
else: else:
data_format = data_format.name data_format = data_format.name
# self.log_verbose('device data format', data_format)
self.log_verbose('device meta file', meta_file) self.log_verbose('device meta file', meta_file)
# data runtime # data runtime
@@ -414,15 +416,15 @@ class DataServer(SocketServer, DataAPI):
c.shutdown() c.shutdown()
c.join() c.join()
self._spi.set_wait_flag(c._device, True) self._spi.set_wait_flag(c._device, True)
data_runtime_process = RecordingProcess('data_server', self.log_verbose, device_id, data_runtime_process = RecordingProcess('data_server', self.log_verbose, device_id,
Queue(), self._queue_ds_dict[int(device_id)], self._queue_msg, meta_file, Queue(), self._queue_ds_dict[int(device_id)], meta_file,
json_stringify(_device), _parameter, _parent, _recording_file_name, json_stringify(_device), _parameter, _parent, _recording_file_name,
data_format, self.socket_mqtt_ip, self.socket_mqtt_port, data_format, self.socket_mqtt_ip, self.socket_mqtt_port,
project_id = project_id, database = self.database_process )
database = self.database_process) # data_runtime_process.start()
# check channel # check channel
if isinstance(data_runtime_process, RecordingProcess) and device_id not in self._available_channel: if isinstance(data_runtime_process, RecordingProcess) and device_id not in self._available_channel:
raise RuntimeError('set device configuration on un-available channel', device_id) raise RuntimeError('set device configuration on un-available channel', device_id)
@@ -616,18 +618,8 @@ class DataServer(SocketServer, DataAPI):
return return
def open_cache_client(self): def open_cache_client(self):
self.log_verbose('open cache client') self._cache_client = None
try: return
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]: def _ensure_cache_client(self) -> Optional[CacheClient]:
if self._cache_client is None: if self._cache_client is None:
@@ -654,7 +646,6 @@ class DataServer(SocketServer, DataAPI):
def whether_to_record(self, device): def whether_to_record(self, device):
# if user click "start", return True; if user click "stop", return False; # if user click "start", return True; if user click "stop", return False;
if device in self._configurations.keys() and self._configurations[device] is not None: if device in self._configurations.keys() and self._configurations[device] is not None:
# print(self._configurations.keys(), ',', device, datetime.now())
return True return True
else: else:
return False return False
@@ -663,9 +654,6 @@ class DataServer(SocketServer, DataAPI):
ret = False ret = False
sync = self.get_spi_obj() sync = self.get_spi_obj()
busy = sync.get_pin_busy()
# print('pin_busy=', busy, device, datetime.now())
if sync.get_pin_mem_req() == sync.get_pin_ram_sel(): if sync.get_pin_mem_req() == sync.get_pin_ram_sel():
spi_data = sync.recv_memory(device) spi_data = sync.recv_memory(device)
signal = sync.get_pin_mem_req() signal = sync.get_pin_mem_req()
@@ -674,49 +662,29 @@ class DataServer(SocketServer, DataAPI):
else: else:
data = None data = None
print('data=None, mem_req!=ram_sel, [', sync.get_pin_mem_req(), ', ', sync.get_pin_ram_sel(), ']', device, datetime.now())
if data is not None: if data is not None:
if self._configurations.get(device, None) != None: if self._configurations[device].queue_flag:
if self._configurations[device].queue_flag: self._configurations[device].queue_flag = False
self._configurations[device].queue_flag = False print("q size= ", self._configurations[device]._queue_rec.qsize())
print("q size= ", self._configurations[device]._queue_rec.qsize())
self._configurations[device].put_rec_queue(data)
if self._configurations.get(device, None) != None: ret = True
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 return ret
def rec_update(self) -> bool: def rec_update(self) -> bool:
ds_db_msg = self._queue_msg.get()
with CacheClient() as client: device_id = self._queue_db_error.get()
if isinstance(ds_db_msg, List): self.mqtt_thread.broadcast_command('runtime error: ' + str(device_id))
if ds_db_msg[0] == 'ds': content = {
client.send_command('device_instruction', ds_db_msg[1], ds_db_msg[2]) 'header': 'device_instruction/0',
else: 'device': device_id,
self.mqtt_thread.broadcast_command('runtime error: ' + str(ds_db_msg)) 'instruction': 'interrupt'
content = { }
'header': 'device_instruction/0', self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
'device': ds_db_msg,
'instruction': 'interrupt'
}
self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
return True return True
def show_data(self, device):
self._configurations[device].put_rec_queue('show_data')
class DataRuntime(metaclass=abc.ABCMeta): class DataRuntime(metaclass=abc.ABCMeta):
__slots__ = ('_server', '_device', '_meta_file', '_data_format', __slots__ = ('_server', '_device', '_meta_file', '_data_format',
@@ -1211,20 +1179,20 @@ class SpiRuntimeThread(ServerThread):
run_time = time() - self._timer run_time = time() - self._timer
real_run_time = run_time
if run_time > 0.045: if run_time > 0.045:
# print('time, recv_data_form_spi_routine_time', time(), run_time) print('time, recv_data_form_spi_routine_time', time(), run_time)
run_time = 0.045 run_time = 0.045
if run_time >= 0.030: if server.sync_started:
print('time, recv_data_form_spi_routine_time >= 0.030', 'device:', c, datetime.now(), time(), real_run_time) event.wait(self._interval - run_time)
sync.set_pin_mem_sel(True)
event.wait(self._interval - run_time) sleep(0.005)
sync.set_pin_mem_sel(True) return
sleep(0.005) else:
event.wait(self._interval - run_time)
return sync.set_pin_mem_sel(True)
self.close()
return
class RecRuntimeThread(ServerThread): class RecRuntimeThread(ServerThread):
def __init__(self, server: DataServer): def __init__(self, server: DataServer):
+86 -199
View File
@@ -15,15 +15,13 @@ from biopro.util.json import JSON_OBJECT
from .socket import ServerThread from .socket import ServerThread
import psycopg2 import psycopg2
from psycopg2.extras import execute_batch # from psycopg2.pool import ThreadedConnectionPool
from multiprocessing import Process, Queue from multiprocessing import Process, Queue
import biopro.server._identify import biopro.server._identify
from time import time
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from biopro.util.logger import calculate_time
_RUNTIME_COMPILE = False _RUNTIME_COMPILE = False
@@ -52,7 +50,7 @@ class DataBaseProcess(Process):
self._api_token = None self._api_token = None
self._api_header = None self._api_header = None
self.log_verbose = log_verbose self.log_verbose = log_verbose
self._executor = ThreadPoolExecutor(max_workers=8) self._executor = ThreadPoolExecutor(max_workers=4)
self._psql_multi_thread = None self._psql_multi_thread = None
self._psql_conn_pool = None self._psql_conn_pool = None
@@ -64,15 +62,6 @@ class DataBaseProcess(Process):
self._is_close = False self._is_close = False
self._meta_write_sql_str = None
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) 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 @property
def db_host(self) -> str: def db_host(self) -> str:
return self._db_host return self._db_host
@@ -160,8 +149,7 @@ class DataBaseProcess(Process):
if callable(getattr(self, do)): if callable(getattr(self, do)):
getattr(self, do)(*args) getattr(self, do)(*args)
return None return None
# @calculate_time(1)
def data_meta_write(self, _data: JSON_OBJECT, device_id, _uuid = None, _id = None): def data_meta_write(self, _data: JSON_OBJECT, device_id, _uuid = None, _id = None):
if _id > 0: if _id > 0:
sql_str = 'UPDATE "public"."recording_data_metas" SET ' sql_str = 'UPDATE "public"."recording_data_metas" SET '
@@ -222,8 +210,7 @@ class DataBaseProcess(Process):
try: try:
sql_cursor.execute(sql_str, sql_set) sql_cursor.execute(sql_str, sql_set)
except BaseException as e: except:
print('meta create error', e)
self._psql_conn.commit() self._psql_conn.commit()
sql_cursor.close() sql_cursor.close()
self._queue_error.put(device_id) self._queue_error.put(device_id)
@@ -250,8 +237,7 @@ class DataBaseProcess(Process):
try: try:
sql_cursor.execute(sql_str, sql_set) sql_cursor.execute(sql_str, sql_set)
except BaseException as e: except:
print('meta update error', e)
self._psql_conn.commit() self._psql_conn.commit()
sql_cursor.close() sql_cursor.close()
self._queue_error.put(device_id) self._queue_error.put(device_id)
@@ -266,7 +252,6 @@ class DataBaseProcess(Process):
# _conn.close() # _conn.close()
return None return None
# @calculate_time()
def data_meta_get(self, _path = None, _id = None): def data_meta_get(self, _path = None, _id = None):
if _id is not None: if _id is not None:
sql_str = 'SELECT * FROM "public"."recording_data_metas" WHERE id = %s' sql_str = 'SELECT * FROM "public"."recording_data_metas" WHERE id = %s'
@@ -301,7 +286,6 @@ class DataBaseProcess(Process):
# _conn.close() # _conn.close()
return None return None
# @calculate_time()
def data_meta_id_get_by_path(self, _path = None): def data_meta_id_get_by_path(self, _path = None):
ret = None ret = None
if _path is not None: if _path is not None:
@@ -323,29 +307,39 @@ class DataBaseProcess(Process):
# _conn.close() # _conn.close()
return None return None
# @calculate_time()
def data_raw_create(self, _data_dict, _channel_list, device_id): def data_raw_create(self, _data_dict, _channel_list, device_id):
if self._data_raw_create_sql_str == None:
sql_str_list = [] for _channel in _channel_list:
key_list = _data_dict[_channel_list[0]].keys() sql_str = 'INSERT INTO "public"."' + str(_channel) + '_recording_data_raws" ('
key_str = ','.join(key_list) sql_set = []
values = ','.join(['%s'] * len(key_list)) for item in _data_dict[_channel].keys():
sql_str_list = ['INSERT INTO "public"."{channel}_recording_data_raws" (', key_str, ') VALUES (', values, ') RETURNING id'] sql_str = sql_str + str(item) + ', '
self._data_raw_create_sql_str = ''.join(sql_str_list) if isinstance(_data_dict[_channel][item], dict):
try: sql_set.append(json_stringify(_data_dict[_channel][item]))
with self._psql_conn as conn: else:
with conn.cursor() as sql_cursor: sql_set.append(str(_data_dict[_channel][item]))
for _channel in _channel_list:
sql_set = list(map(lambda x: str(x), _data_dict[_channel].values())) sql_str = sql_str[0:-2] + ') VALUES ('
sql_cursor.execute(self._data_raw_create_sql_str.format(channel = _channel), sql_set) for item in _data_dict[_channel].keys():
self._queue_ds[int(device_id)].put(['update_raw_dict_id', _data_dict[_channel]['uuid'], _channel, int(sql_cursor.fetchone()[0])]) sql_str = sql_str + '%s, '
except (psycopg2.Error, RuntimeError) as e:
print('data_raw_create error', e) sql_str = sql_str[0:-2] + ') RETURNING id'
self._queue_error.put(device_id) sql_cursor = self._psql_conn.cursor()
try:
sql_cursor.execute(sql_str, sql_set)
except RuntimeError as e:
print('data_raw_create error', e)
self._psql_conn.commit()
sql_cursor.close()
self._queue_error.put(device_id)
finally:
self._queue_ds[int(device_id)].put(['update_raw_dict_id', _data_dict[_channel]['uuid'], _channel, int(sql_cursor.fetchone()[0])])
self._psql_conn.commit()
sql_cursor.close()
return True return True
# @calculate_time()
def data_raw_update(self, _id, _channel, _data: JSON_OBJECT): def data_raw_update(self, _id, _channel, _data: JSON_OBJECT):
sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_raws" SET ' sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_raws" SET '
sql_set = [] sql_set = []
@@ -363,36 +357,30 @@ class DataBaseProcess(Process):
sql_cursor.execute(sql_str, sql_set) sql_cursor.execute(sql_str, sql_set)
self._psql_conn.commit() self._psql_conn.commit()
sql_cursor.close() sql_cursor.close()
return None return None
# @calculate_time() def data_raw_recording(self, _id_dict, _channel_list, _data_dict):
def data_raw_recording(self, _id_dict, _channel_list, _data_dict, _id_db_save): for _channel in _channel_list:
try: compressed_value = _data_dict[_channel] + '"***"'
with self._psql_conn as conn: # compressed_value = bytes(_data_dict[_channel],'utf-8')
with conn.cursor() as sql_cursor: # compressed_value = zlib.compress(compressed_value)
para_list = [] # compressed_value = base64.b64encode(compressed_value).decode() + '"***"'
for _channel in _channel_list: # compressed_value = _data + '"***"'
temp_id = _id_dict[_channel] if _id_dict[_channel] != 0 else _id_db_save[_channel] sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_raws" SET data = concat(data, %s) where id = %s'
para_list.append([_channel, _data_dict[_channel] + '"***"', temp_id]) sql_set = [compressed_value, _id_dict[_channel]]
execute_batch(sql_cursor, self._data_raw_recording_sql_str, para_list)
except psycopg2.Error as e: sql_cursor = self._psql_conn.cursor()
print('recording error', e) sql_cursor.execute(sql_str, sql_set)
self._psql_conn.commit()
sql_cursor.close()
del _data_dict[_channel]
del compressed_value
gc.collect()
# _conn.close()
return None return None
# @calculate_time(1)
def data_raw_recording_new(self, _id_dict, _channel_list, _data_dict, _end_time_dict, _size_dict):
try:
para_list = []
for _channel in _channel_list:
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._new_data_raw_recording_sql_str, para_list)
except psycopg2.Error as e:
print('recording error', e)
return None
# @calculate_time()
def data_mini_create(self, _data_dict, _channel_list, _scale, device_id): def data_mini_create(self, _data_dict, _channel_list, _scale, device_id):
for _channel in _channel_list: for _channel in _channel_list:
sql_str = 'INSERT INTO "public"."' + str(_channel) + '_recording_data_minis" (' sql_str = 'INSERT INTO "public"."' + str(_channel) + '_recording_data_minis" ('
@@ -426,43 +414,42 @@ class DataBaseProcess(Process):
# _conn.close() # _conn.close()
return True return True
# @calculate_time()
def data_mini_recording(self, _id_dict, _channel_list, _start_time_dict, _data_mean_dict): def data_mini_recording(self, _id_dict, _channel_list, _start_time_dict, _data_mean_dict):
with self._psql_conn as conn:
with self._psql_conn.cursor() as sql_cursor:
for _channel in _channel_list:
data = _data_mean_dict.get(_channel)
if data is not None:
if len(data) != 0:
str_mean = [str(int) for int in _data_mean_dict[_channel]]
data_mean = _start_time_dict[_channel] + ' ' + ' '.join(str_mean)
sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_minis" SET data_mean = concat(data_mean, %s) where id = %s'
sql_set = [data_mean + '"***"', _id_dict[_channel]]
sql_cursor.execute(sql_str, sql_set)
del str_mean, data_mean
for _channel in _channel_list: for _channel in _channel_list:
del _start_time_dict[_channel], _data_mean_dict[_channel] data = _data_mean_dict.get(_channel)
gc.collect() if data is not None:
if len(data) != 0:
str_mean = [str(int) for int in _data_mean_dict[_channel]]
data_mean = _start_time_dict[_channel] + ' ' + ' '.join(str_mean)
# data_random = _start_time_dict[_channel] + ' ' + ' '.join(_data_random_dict[_channel])
# data_bar = _start_time_dict[_channel] + ' ' + ' '.join(_data_bar_dict[_channel])
compressed_mean = data_mean + '"***"'
# compressed_mean = bytes(data_mean,'utf-8')
# compressed_mean = zlib.compress(compressed_mean)
# compressed_mean = base64.b64encode(compressed_mean).decode() + '"***"'
# compressed_random = bytes(data_bar,'utf-8')
# compressed_random = zlib.compress(compressed_random)
# compressed_random = base64.b64encode(compressed_random).decode() + '"***"'
# compressed_random = bytes(data_random,'utf-8')
# compressed_random = zlib.compress(compressed_random)
# compressed_random = base64.b64encode(compressed_random).decode() + '"***"'
sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_minis" SET data_mean = concat(data_mean, %s) where id = %s'
sql_set = [compressed_mean, _id_dict[_channel]]
sql_cursor = self._psql_conn.cursor()
sql_cursor.execute(sql_str, sql_set)
self._psql_conn.commit()
sql_cursor.close()
del str_mean, _start_time_dict[_channel], _data_mean_dict[_channel], data_mean, compressed_mean
gc.collect()
return True return True
# @calculate_time(1)
def data_mini_recording_new(self, _id_dict, _channel_list, _data_mean_dict):
try:
para_list = []
for _channel in _channel_list:
para_list.append([_channel, _data_mean_dict[_channel], _id_dict[_channel]])
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
execute_batch(sql_cursor, self._data_mini_recording_sql_str, para_list)
except psycopg2.Error as e:
print('recording error', e)
return None
# @calculate_time()
def data_mini_update(self, _id, _channel, _data: JSON_OBJECT): def data_mini_update(self, _id, _channel, _data: JSON_OBJECT):
sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_minis" SET ' sql_str = 'UPDATE "public"."' + str(_channel) + '_recording_data_minis" SET '
sql_set = [] sql_set = []
@@ -482,103 +469,3 @@ class DataBaseProcess(Process):
sql_cursor.close() sql_cursor.close()
return True return True
def data_meta_update_raw_empty_channel(self, meta_id, channel_list):
try:
sql_str = "UPDATE recording_data_metas SET raw_data = jsonb_set(raw_data, '{%s}', '[]'), mini_data = jsonb_set(mini_data, '{%s}', '{\"10\": [], \"100\": [], \"1000\": []}') WHERE id = %s;"
sql_set = []
for channel in channel_list:
sql_set.append([channel, channel, meta_id])
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
execute_batch(sql_cursor, sql_str, sql_set)
except psycopg2.Error as e:
print('e', e)
return True
# @calculate_time()
def data_meta_update_channel_raw_id(self, meta_id, channel, raw_id):
try:
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
sql_str = "UPDATE recording_data_metas SET raw_data = jsonb_insert(raw_data, '{%s, -1}', '%s', TRUE) WHERE id = %s;"
sql_set = [channel, raw_id, 571]
sql_cursor.execute(sql_str, sql_set)
except psycopg2.Error as e:
print('e', e)
return True
# @calculate_time()
def data_meta_update_all_channel_raw_id(self, meta_id:str, raw_id_dict:dict):
try:
sql_str = "UPDATE recording_data_metas SET raw_data = jsonb_insert(raw_data, '{%s, -1}', '%s', TRUE) WHERE id = %s;"
sql_set = []
for ch in raw_id_dict.keys():
sql_set.append([ch, raw_id_dict[ch], meta_id])
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
execute_batch(sql_cursor, sql_str, sql_set)
except psycopg2.Error as e:
print('e', e)
return True
def data_meta_update_channel_mini_id(self, meta_id, channel, scale, mini_id):
try:
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
sql_str = "UPDATE recording_data_metas SET mini_data = jsonb_insert(mini_data, '{%s, %s, -1}', '%s', TRUE) WHERE id = %s;"
sql_set = [channel, scale, mini_id, meta_id]
sql_cursor.execute(sql_str, sql_set)
except psycopg2.Error as e:
print('e', e)
return True
# @calculate_time()
def data_meta_update_all_channel_mini_id(self, meta_id, mini_id_dict):
try:
sql_str = "UPDATE recording_data_metas SET mini_data = jsonb_insert(mini_data, '{%s, %s, -1}', '%s', TRUE) WHERE id = %s;"
sql_set = []
for ch in mini_id_dict.keys():
for scale in mini_id_dict[ch].keys():
sql_set.append([ch, scale, mini_id_dict[ch][scale], meta_id])
with self._psql_conn as conn:
with conn.cursor() as sql_cursor:
execute_batch(sql_cursor, sql_str, sql_set)
except psycopg2.Error as e:
print('e', e)
return True
def project_insert(self, device_id, _data):
sql_str = 'INSERT INTO "public"."project_metas" ('
sql_set = []
for item in _data.keys():
sql_str = sql_str + str(item) + ', '
if isinstance(_data[item], dict) or isinstance(_data[item], list):
sql_set.append(json_stringify(_data[item]))
else:
sql_set.append(_data[item])
sql_str = sql_str[0:-2] + ') VALUES ('
for item in _data.keys():
sql_str = sql_str + '%s, '
sql_str = sql_str[0:-2] + ') RETURNING id'
sql_cursor = self._psql_conn.cursor()
try:
sql_cursor.execute(sql_str, sql_set)
except BaseException as e:
print('insert error', e)
self._psql_conn.commit()
sql_cursor.close()
self._queue_error.put()
finally:
self._queue_ds[int(device_id)].put(['project_id', int(sql_cursor.fetchone()[0])])
self._psql_conn.commit()
sql_cursor.close()
+558 -117
View File
@@ -1,7 +1,6 @@
from time import sleep from time import sleep
from typing import Iterable from typing import Iterable
from datetime import datetime from datetime import datetime
import json
import biopro.impl.vcgencmd as vcg import biopro.impl.vcgencmd as vcg
from biopro.data import DataServerOptions, DataAPI from biopro.data import DataServerOptions, DataAPI
@@ -31,12 +30,8 @@ from .api import ApiRequests
from biopro.api.auth import AuthAPI from biopro.api.auth import AuthAPI
from biopro.api.controller import ControllerAPI from biopro.api.controller import ControllerAPI
from biopro.api.device import DeviceAPI from biopro.api.device import DeviceAPI
from biopro.project.project_manager import ProjectManager ### do not import Websocket mode
from biopro.db.base import Base, Session, engine ###from .websocket import WebsocketThread
from biopro.db.project_report import ProjectReport
from biopro.db.project_meta import MetaProjectInfo
from biopro.db.device import Device
_RUNTIME_COMPILE = False _RUNTIME_COMPILE = False
@@ -47,7 +42,7 @@ def _fc(c):
return c % len(FORE_COLOR) + FORE_COLOR[0] return c % len(FORE_COLOR) + FORE_COLOR[0]
class ControlServer(SocketServer, ControlServerAPI): class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler):
"""Data Server""" """Data Server"""
def __init__(self, *options): def __init__(self, *options):
@@ -64,7 +59,7 @@ class ControlServer(SocketServer, ControlServerAPI):
:param options: server options :param options: server options
""" """
super().__init__((ControlAPI), 'Controller', self.ADDR, super().__init__((ControlAPI, CacheAPI), 'Controller', self.ADDR,
socket_listen=10, socket_listen=10,
thread_pool_count=16) thread_pool_count=16)
@@ -91,6 +86,21 @@ class ControlServer(SocketServer, ControlServerAPI):
data_options.repository = device_option.repository data_options.repository = device_option.repository
self.data_server = DataServerProcess(data_options) 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 # remote server / MQTT
self.mqtt_thread = None self.mqtt_thread = None
if not c.flag_disable_remote_server: if not c.flag_disable_remote_server:
@@ -101,6 +111,17 @@ class ControlServer(SocketServer, ControlServerAPI):
self.log_verbose, self.log_verbose,
'MQTT-control-server') '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 # LED control
self.led_thread = LEDControlThread(disable=c.flag_disable_led_control, self.led_thread = LEDControlThread(disable=c.flag_disable_led_control,
led_count=c.led_count, led_count=c.led_count,
@@ -109,8 +130,6 @@ class ControlServer(SocketServer, ControlServerAPI):
# device detect # device detect
self._detect_device_thread = RoutineConnectDeviceThread(self._device_manager, self.mqtt_thread, self.led_thread, 3) 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, self.log_verbose)
if not c.flag_disable_led_control: if not c.flag_disable_led_control:
self.led_thread.set_state(LED.COLOR_RED) self.led_thread.set_state(LED.COLOR_RED)
@@ -121,8 +140,10 @@ class ControlServer(SocketServer, ControlServerAPI):
self.ups_thread = UPSDetectThread(self, c.flag_ups_wait_time) self.ups_thread = UPSDetectThread(self, c.flag_ups_wait_time)
# user session # user session
# self._session = {} self._session = {}
# self._user_session_expire = c.user_session_expire_days * 24 * 60 * 60 # sec self._user_session_expire = c.user_session_expire_days * 24 * 60 * 60 # sec
# temp use variable
# indicate those device together # indicate those device together
self._device_instruction_group = None self._device_instruction_group = None
@@ -133,9 +154,7 @@ class ControlServer(SocketServer, ControlServerAPI):
# _api request build # _api request build
self._api = ApiRequests() self._api = ApiRequests()
# sqlalchemy
Base.metadata.create_all(engine)
self._session = Session()
# init method # init method
@@ -174,11 +193,6 @@ class ControlServer(SocketServer, ControlServerAPI):
def device_manager(self) -> DeviceManager: def device_manager(self) -> DeviceManager:
return self._device_manager return self._device_manager
@property
def project_manager(self) -> ProjectManager:
return self._project_manager
# server setup # server setup
def setup(self) -> None: def setup(self) -> None:
@@ -228,16 +242,32 @@ class ControlServer(SocketServer, ControlServerAPI):
# device manager reset # device manager reset
self.submit_thread(self._setup_device) self.submit_thread(self._setup_device)
# self.submit_thread(self._setup_controller)
self._setup_controller() self._setup_controller()
# self.run_thread(self._detect_device_thread) # 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 # broadcast greeting
self.broadcast_command(DataMessage.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 # remote server
if self.mqtt_thread is not None: if self.mqtt_thread is not None:
self.mqtt_thread.start() self.mqtt_thread.start()
# self.run_thread(self.mqtt_thread)
def _setup_file_manager(self): def _setup_file_manager(self):
self.log_verbose('setup file manager') self.log_verbose('setup file manager')
@@ -412,14 +442,90 @@ class ControlServer(SocketServer, ControlServerAPI):
self.broadcast_command('shutdown') self.broadcast_command('shutdown')
self.data_server.stop_process(external_interrupt) self.data_server.stop_process(external_interrupt)
self._stop_data_stream_thread()
self.device_manager.shutdown() self.device_manager.shutdown()
self.file_manager.shutdown(umount_external=True) self.file_manager.shutdown(umount_external=True)
self.cache_manager.shutdown()
### self.websocket_thread.close()
self.led_thread.close() self.led_thread.close()
if self.mqtt_thread is not None: if self.mqtt_thread is not None:
self.mqtt_thread.shutdown() 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]: def _post_device_list(self) -> List[CompletedDevice]:
"""general post action for getting list of connected devices. """general post action for getting list of connected devices.
@@ -756,6 +862,12 @@ class ControlServer(SocketServer, ControlServerAPI):
def get_device_info(self, device: int) -> Optional[CompletedDevice]: def get_device_info(self, device: int) -> Optional[CompletedDevice]:
return super().get_device_info(device) 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 @logging_info
def device_instruction_all(self, device: int) -> List[str]: def device_instruction_all(self, device: int) -> List[str]:
return self.device_manager.list_device_instruction(device) return self.device_manager.list_device_instruction(device)
@@ -827,21 +939,12 @@ class ControlServer(SocketServer, ControlServerAPI):
return True return True
@logging_info @logging_info
def device_battery(self, device:int) -> List[int]: def device_battery(self) -> List[int]:
try: device_list = self.device_manager.list_device()
battery = {} if len(device_list) > 0:
if device == 'all': battery_list = list(map(lambda x: self.device_manager.get_device(x).battery, device_list))
device_list = self.device_manager.list_device()
if len(device_list) > 0: return battery_list
battery = list(map(lambda x: {"device": x.memory_board, "battery":self.device_manager.get_device(x).battery}, device_list))
else:
battery = {
"device": device,
"battery": self.device_manager.get_device(device).battery
}
except AttributeError as e:
print('device_battery', device, 'not found', e)
return battery
@logging_info @logging_info
def device_parent(self, device: int, content: Optional[str] = None) -> List[int]: def device_parent(self, device: int, content: Optional[str] = None) -> List[int]:
@@ -863,32 +966,20 @@ class ControlServer(SocketServer, ControlServerAPI):
@logging_info @logging_info
def device_update_calibration(self, device: int) -> Union[bool, str]: def device_update_calibration(self, device: int) -> Union[bool, str]:
connect_device = self.device_manager.get_device(device) connect_device = self.device_manager.get_device(device)
if connect_device == None: mac_address = connect_device.mac_address
return
mac_address = address_str(connect_device.mac_address)
device = Device.get_device({"mac_address": mac_address})
try: try:
if connect_device.library.name.startswith('Elite_EIS'): if connect_device.library.name.startswith('Neulive3.'):
# update calibration version connect_device.calibration_info('NeuliveThreeOne')
connect_device._device.update_cali_version() elif connect_device.library.name.startswith('Neulive'):
# check if is first time or calibration version is different connect_device.calibration_info('TDC4VC')
if connect_device._device._cali_version == -1 or (device.calibration_version != connect_device._device._cali_version): elif connect_device.library.name.startswith('EliteEIS'):
# get calibration info from device connect_device.calibration_info('EISZeroOne')
connect_device.calibration_info('EISZeroOne')
# update_calibration_info
Device.update_device(
{ "mac_address": mac_address },
{
'calibration': connect_device.calibration ,
'calibration_version': connect_device._device._cali_version
}
)
else:
connect_device._device._coeff = device.calibration
except BaseException as e: except BaseException as e:
# reset device info in db # reset device info in db
DeviceAPI.updateByMac( DeviceAPI.updateByMac(
mac_address, address_str(mac_address),
{ {
'connect_id': -1, 'connect_id': -1,
'connect_status': 'idle', 'connect_status': 'idle',
@@ -900,6 +991,14 @@ class ControlServer(SocketServer, ControlServerAPI):
print(e) print(e)
return False return False
else: else:
# update_calibration_info
DeviceAPI.updateByMac(
address_str(mac_address),
{
'calibration': str(connect_device.calibration)
}
)
# DeviceAPI.updateByAttrs('connect_status', 'update', {"connect_status": 'connect'})
return True return True
@logging_info @logging_info
@@ -942,6 +1041,8 @@ class ControlServer(SocketServer, ControlServerAPI):
@logging_info @logging_info
def device_parameter(self, device: int, parameter: str, content: Optional[str] = None) -> Any: 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) return self.device_manager.handle_device_parameter_command(device, parameter, content)
@logging_info @logging_info
@@ -1033,44 +1134,249 @@ class ControlServer(SocketServer, ControlServerAPI):
def device_history_delete(self, user: str, device: Optional[int]) -> bool: def device_history_delete(self, user: str, device: Optional[int]) -> bool:
raise NotImplementedError() raise NotImplementedError()
@logging_info # server provide method implement : file manager functions
def run_project(self, project) -> bool:
if project is not None:
project = self._project_manager.create(project)
with self._session as session:
project_report = ProjectReport(
name = project.name,
uuid = project.uuid,
cycle = project.cycle,
task = project.task_list,
device = project.device
)
session.add(project_report)
session.commit()
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 set_project(self, project, content) -> bool:
if project is not None:
self._project_manager.set_project(project, content)
return True
@logging_info
def set_project_cycle(self, project, index, content) -> bool:
if project is not None:
self._project_manager.set_project_cycle(project, index, content)
return True
@logging_info @logging_info
def get_running_project(self) -> bool: def file_clean_storage(self) -> bool:
return self._project_manager.get() 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 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 @logging_info
def device_internal_command(self, device: int, oper: str, value: Any) -> bool: def device_internal_command(self, device: int, oper: str, value: Any) -> bool:
@@ -1087,10 +1393,8 @@ class ControlServer(SocketServer, ControlServerAPI):
return True return True
elif oper == InternalInstruction.PREDEFINED_DISABLE_CACHE: elif oper == InternalInstruction.PREDEFINED_DISABLE_CACHE:
return True if device is not None:
self._device_set_disable_cache(device, value)
elif oper == InternalInstruction.PREDEFINED_IDLE:
device.status = 1
return True return True
else: else:
@@ -1106,8 +1410,6 @@ class ControlServer(SocketServer, ControlServerAPI):
self.log_verbose('update data format', value) self.log_verbose('update data format', value)
client = self.data_server.client() client = self.data_server.client()
project = None
project_meta_id = None
if client is not None: if client is not None:
info = self.file_manager.use(device) info = self.file_manager.use(device)
@@ -1118,13 +1420,9 @@ class ControlServer(SocketServer, ControlServerAPI):
filename = self._file_save_cache.get(device.device_id, self.TEMP_FILENAME_EXPR) filename = self._file_save_cache.get(device.device_id, self.TEMP_FILENAME_EXPR)
filename = format_filename(filename, device) filename = format_filename(filename, device)
info = self.file_manager.save(device, filename) info = self.file_manager.save(device, filename)
if device.occupied_by_project != None:
project = self.project_manager.get(device.occupied_by_project).info_pass_data_server()
project_meta_id = MetaProjectInfo.create_project_meta(project)
with client: with client:
client.update_device_configuration(device, info.meta_file, value, project_meta_id) client.update_device_configuration(device, info.meta_file, value)
def _device_set_disable_cache(self, device: CompletedDevice, disable): def _device_set_disable_cache(self, device: CompletedDevice, disable):
if disable: if disable:
@@ -1148,6 +1446,10 @@ class ControlServer(SocketServer, ControlServerAPI):
def _device_set_sync(self, device: CompletedDevice, sync: bool): def _device_set_sync(self, device: CompletedDevice, sync: bool):
if sync: if sync:
self.log_verbose('start sync', device.device_id)
self.cache_manager.drop_data(device)
client = self.data_server.client() client = self.data_server.client()
if client is not None: if client is not None:
info = self.file_manager.use(device) info = self.file_manager.use(device)
@@ -1158,13 +1460,22 @@ class ControlServer(SocketServer, ControlServerAPI):
filename = format_filename(self.TEMP_FILENAME_EXPR, device) filename = format_filename(self.TEMP_FILENAME_EXPR, device)
info = self.file_manager.save(device, filename) info = self.file_manager.save(device, filename)
client.update_device_configuration(device, info.meta_file, RawDataDecoder()) client.update_device_configuration(device, info.meta_file, RawDataDecoder())
print('***456')
if self._device_instruction_group is None: if self._device_instruction_group is None:
client.start_sync(device) client.start_sync(device)
device.status = 1
else:
if self._device_instruction_group is None: if self._device_instruction_group is None:
# broadcast
self.broadcast_command(DataMessage.start(device.device_id))
else:
self.log_verbose('stop sync', device.device_id)
if self._device_instruction_group is None:
start_sync = False start_sync = False
client = self.data_server.client() client = self.data_server.client()
if client is not None: if client is not None:
with client: with client:
@@ -1177,10 +1488,12 @@ class ControlServer(SocketServer, ControlServerAPI):
if start_sync: if start_sync:
# unset file info, but keep file path cache # unset file info, but keep file path cache
self.file_manager.unset(device.device_id) self.file_manager.unset(device.device_id)
device.status = 0
# broadcast
self.broadcast_command(DataMessage.stop(device.device_id))
# server provide method implement : websocket broadcast functions # server provide method implement : websocket broadcast functions
@logging_info @logging_info
def broadcast_command(self, command: str, device: Optional[int] = None): def broadcast_command(self, command: str, device: Optional[int] = None):
self.log_verbose(pc('broadcast_command', GREEN), self.log_verbose(pc('broadcast_command', GREEN),
@@ -1190,6 +1503,96 @@ class ControlServer(SocketServer, ControlServerAPI):
if self.mqtt_thread is not None: if self.mqtt_thread is not None:
self.mqtt_thread.broadcast_command(command, device=device) 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 # noinspection PyShadowingNames
def _handle_command_response(self, handler: DataMessageHandler, command: Optional[str]): def _handle_command_response(self, handler: DataMessageHandler, command: Optional[str]):
""" """
@@ -1211,7 +1614,29 @@ class ControlServer(SocketServer, ControlServerAPI):
else: else:
self.cache_manager.get_data_message(handler, response) 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 # led function
def get_led_state(self) -> str: def get_led_state(self) -> str:
return self.led_thread.state return self.led_thread.state
@@ -1219,6 +1644,7 @@ class ControlServer(SocketServer, ControlServerAPI):
self.led_thread.set_state(state) self.led_thread.set_state(state)
# hardware send test # hardware send test
if not _RUNTIME_COMPILE: if not _RUNTIME_COMPILE:
def _hardware_send_test(self, options: Dict[str, str]): def _hardware_send_test(self, options: Dict[str, str]):
@@ -1261,9 +1687,30 @@ class ControlServer(SocketServer, ControlServerAPI):
'export_root': str(self.file_manager.export_root) '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: if section is None or 'mqtt' in section:
ret['mqtt'] = None if self.mqtt_thread is None else self.mqtt_thread.hardware_test() 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: if section is None or 'led' in section:
ret['led'] = { ret['led'] = {
'state_all': list(LED.COLOR.keys()), 'state_all': list(LED.COLOR.keys()),
@@ -1391,13 +1838,7 @@ class ControlServer(SocketServer, ControlServerAPI):
def _hardware_send_test_set(self, options: Dict[str, str]) -> bool: def _hardware_send_test_set(self, options: Dict[str, str]) -> bool:
# TODO write options files # TODO write options files
return False return False
@logging_info
def show_device_data(self, device: int):
client = self.data_server.client()
if client is not None:
with client:
client.show_data(device)
class _RandomCrashThread(ServerThread): class _RandomCrashThread(ServerThread):
def __init__(self): def __init__(self):
+96 -122
View File
@@ -41,12 +41,10 @@ class RecordingProcess(Process):
connect to DB and send sql command connect to DB and send sql command
""" """
def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, queue_msg, meta_path, def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, meta_path,
device_info, parameter, parent, recording_file_name, data_format, device_info, parameter, parent, recording_file_name, data_format,
mqtt_ip, mqtt_port, mqtt_ip, mqtt_port,
name = 'Recording-Process', name = 'Recording-Process', database = None):
project_id = None,
database = None):
super(RecordingProcess, self).__init__(name = name) super(RecordingProcess, self).__init__(name = name)
self._database = database self._database = database
@@ -59,7 +57,6 @@ class RecordingProcess(Process):
self._meta_file._parent = parent self._meta_file._parent = parent
self._meta_file._recording_file_name = recording_file_name self._meta_file._recording_file_name = recording_file_name
self._meta_file._device_id = device_id self._meta_file._device_id = device_id
self._meta_file._project_id = project_id
print(self._meta_file._device, self._meta_file._parameter, self._meta_file._parent, self._meta_file._filepath) print(self._meta_file._device, self._meta_file._parameter, self._meta_file._parent, self._meta_file._filepath)
self._meta_file.write(self._database) self._meta_file.write(self._database)
@@ -85,7 +82,6 @@ class RecordingProcess(Process):
self._queue_rec = queue_rec self._queue_rec = queue_rec
self._queue_ds = queue_ds self._queue_ds = queue_ds
self._queue_msg = queue_msg
self.queue_flag = True self.queue_flag = True
self._is_close = True self._is_close = True
@@ -102,14 +98,6 @@ class RecordingProcess(Process):
self._last_cnt = [-1, -1, -1, -1, -1, -1, -1, -1] self._last_cnt = [-1, -1, -1, -1, -1, -1, -1, -1]
self._wrong_cnt = 0 self._wrong_cnt = 0
# elite data length
self._elite_data_len = 40
self._mem_header_len = 3
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: def ensure_data_format(self) -> DataDecodeFormat:
if isinstance(self._data_format, (str, bytes)): if isinstance(self._data_format, (str, bytes)):
@@ -139,9 +127,9 @@ class RecordingProcess(Process):
decoder._prev_data = self._prev_data decoder._prev_data = self._prev_data
decoder._prev_delta_time = self._prev_delta_time decoder._prev_delta_time = self._prev_delta_time
decoder._prev_time_stamp = self._prev_time_stamp decoder._prev_time_stamp = self._prev_time_stamp
elif isinstance(decoder, I4V4Z4T4DataDecoder): # elif isinstance(decoder, I4V4Z4T4DataDecoder):
# get cycle_time from meta file # get cycle_time from meta file
decoder._mode = self._meta_file.configuration.get_parameter('MODE') # decoder._mode = self._meta_file.configuration.MODE
# decoder._cycle_start_time = self._cycle_start_time # decoder._cycle_start_time = self._cycle_start_time
elif isinstance(decoder, NeuliveThreeOneDataDecoder): elif isinstance(decoder, NeuliveThreeOneDataDecoder):
# get amp_gain from meta file # get amp_gain from meta file
@@ -157,7 +145,11 @@ class RecordingProcess(Process):
decoder._prev_time_stamp = self._prev_time_stamp decoder._prev_time_stamp = self._prev_time_stamp
elif isinstance(decoder, EISZeroOneDataDecoder): elif isinstance(decoder, EISZeroOneDataDecoder):
# get amp_gain from meta file # 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._mode = self._meta_file.configuration.get_parameter('MODE')
decoder._freq_start = self._meta_file.configuration.get_parameter('FREQ_START')
decoder._freq_stop = self._meta_file.configuration.get_parameter('FREQ_STOP')
return decoder return decoder
@@ -168,11 +160,7 @@ class RecordingProcess(Process):
""" """
q = None q = None
try: try:
# now = time()
q = self._queue_rec.get() q = self._queue_rec.get()
# get_queue_time_cost = time() - now
# if get_queue_time_cost > 2:
# print('get queue cost time', get_queue_time_cost)
except: except:
return False return False
finally: finally:
@@ -185,8 +173,6 @@ class RecordingProcess(Process):
self.final_write() self.final_write()
self.is_closed = True self.is_closed = True
return False return False
elif q == 'show_data':
self._decoder._show_data = not self._decoder._show_data
else: else:
self.rec_update() self.rec_update()
self.sync_data(q) self.sync_data(q)
@@ -200,17 +186,18 @@ class RecordingProcess(Process):
current_time = time() current_time = time()
if self._timer is not None: if self._timer is not None:
if current_time - self._timer > 1.5: if current_time - self._timer > 1.5:
print('time, sync_data routine_time', 'device:', self._device, datetime.now(), current_time, current_time - self._timer) print('time, sync_data routine_time', current_time, current_time - self._timer)
self._isTimeOut = True self._isTimeOut = True
self._timer = current_time self._timer = current_time
decoder = self.data_format()
# print('sync_data') # print('sync_data')
# print('data',data) # print('data',data)
# print('server/data', self._prev_delta_time,self._prev_time_stamp, self._prev_data) # print('server/data', self._prev_delta_time,self._prev_time_stamp, self._prev_data)
if data is None or len(data) == 0: if data is None or len(data) == 0:
result = self._decoder.decode(b'') result = decoder.decode(b'')
if result is not None: if result is not None:
ret = result ret = result
@@ -224,13 +211,25 @@ class RecordingProcess(Process):
for offset, section in self._foreach_data_section(data): for offset, section in self._foreach_data_section(data):
# for section in self._neu_foreach_data_section(data): # for section in self._neu_foreach_data_section(data):
result = self._decoder.decode(section) result = decoder.decode(section)
# if self._isTimeOut: # if self._isTimeOut:
# try: # try:
# print('result: ', result.data_size) # print('result: ', result.data_size)
# except: # except:
# pass # 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)
# self.server.stop_sync(self.device)
except RuntimeError as e:
print(e)
if result is not None: if result is not None:
## ##
if len(self._mqtt_send_data_ch_level) == 0: if len(self._mqtt_send_data_ch_level) == 0:
@@ -247,27 +246,9 @@ class RecordingProcess(Process):
self._writer.channels_update(ret[0].channels()) self._writer.channels_update(ret[0].channels())
self._writer.write(ret, self._mqtt_send_data_ch_level) self._writer.write(ret, self._mqtt_send_data_ch_level)
# print('write time: ', time() - ctime1) # print('write time: ', time() - ctime1)
# print(ret) # print(ret)
try:
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)
del ret del ret
del data del data
return return
@@ -305,8 +286,7 @@ class RecordingProcess(Process):
self._last_cnt[7] = head_counter - 1 self._last_cnt[7] = head_counter - 1
last_data_cnt = self._last_cnt[7] last_data_cnt = self._last_cnt[7]
# dont save to section when head or id is wrong if (head != 255 or device_id != device):
if (head != 255 or device_id != device or head_counter > 255 or head_counter < 0):
save = False save = False
last_data_cnt = last_data_cnt + 1 last_data_cnt = last_data_cnt + 1
@@ -348,58 +328,61 @@ class RecordingProcess(Process):
return save return save
def _check_jump_ram(self, raw_data: bytes, colum_now: int, colum_total: int, save: bool, device: int, data_len: int): def _check_jump_ram(self, raw_data: bytes, colum_now: int, colum_total: int, save: bool, device: int):
col = colum_now i = colum_now
save = False
if raw_data[col][0] != 255 or raw_data[col][3] != device: if (raw_data[i][0] == 255 and raw_data[i][3] == device):
return save if colum_total < 3:
print('this ram data < 3 records, need to fix, colum_total = ', colum_total)
if colum_total == 1:
print('raw_data[0]', raw_data[0])
if colum_total == 1: elif colum_total == 2:
print('this ram data < 3 records, colum_total = ', colum_total) print('raw_data[0]', raw_data[0])
print('raw_data[0]', list(raw_data[0])) print('raw_data[1]', raw_data[1])
save = True
# return save
elif colum_total == 2: else:
print('this ram data < 3 records, colum_total = ', colum_total)
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
elif colum_total >= 3: if((raw_data[i + 1][1] - raw_data[i][1] == 1 and raw_data[i + 2][1] - raw_data[i + 1][1] == 1) or
if (raw_data[col + 1][1] - raw_data[col][1] == 1 and raw_data[col + 2][1] - raw_data[col + 1][1] == 1) or\ (raw_data[i][1] == 255 and raw_data[i + 1][1] == 0 and raw_data[i + 2][1] == 1) or
(raw_data[col][1] == 255 and raw_data[col + 1][1] == 0 and raw_data[col + 2][1] == 1) or\ (raw_data[i][1] == 254 and raw_data[i + 1][1] == 255 and raw_data[i + 2][1] == 0)):
(raw_data[col][1] == 254 and raw_data[col + 1][1] == 255 and raw_data[col + 2][1] == 0):
save = True
if save == True: if ((raw_data[i][1] == 255 and raw_data[i + 1][1] == 0 and raw_data[i + 2][1] == 1) or
self._skip_ram_cnt += 1 (raw_data[i][1] == 254 and raw_data[i + 1][1] == 255 and raw_data[i + 2][1] == 0)):
last_data_cnt = raw_data[col][1] print('cnt = [254, 255, 0] or [255, 0, 1]')
if (device == 4):
print('self._last_cnt[4]:', self._last_cnt[4], ', last_data_cnt:', last_data_cnt)
if (device == 5):
print('self._last_cnt[5]:', self._last_cnt[5], ', last_data_cnt:', last_data_cnt)
if (device == 6):
print('self._last_cnt[6]:', self._last_cnt[6], ', last_data_cnt:', last_data_cnt)
if (device == 7):
print('self._last_cnt[7]:', self._last_cnt[7], ', last_data_cnt:', last_data_cnt)
print('skip RAM, but data is right, solve data', list(raw_data[col][0:3]),'|', list(raw_data[colum_total-1][0:3]), 'skip Ram', self._skip_ram_cnt, 'times \n')
if (device == 4): save = True
self._last_cnt[4] = last_data_cnt self._skip_ram_cnt += 1
last_data_cnt = raw_data[i][1]
if (device == 4):
print('self._last_cnt[4]:', self._last_cnt[4], ', last_data_cnt:', last_data_cnt)
if (device == 5):
print('self._last_cnt[5]:', self._last_cnt[5], ', last_data_cnt:', last_data_cnt)
if (device == 6):
print('self._last_cnt[6]:', self._last_cnt[6], ', last_data_cnt:', last_data_cnt)
if (device == 7):
print('self._last_cnt[7]:', self._last_cnt[7], ', last_data_cnt:', last_data_cnt)
print('skip RAM, but data is right, solve data', list(raw_data[i][0:3]), 'skip Ram', self._skip_ram_cnt, 'times')
# print('data:')
# print('|', list(raw_data[0]))
# print('|', list(raw_data[1]))
# print('| ... ... ...')
# print('| ... ... ...')
# print('|', list(raw_data[colum_total-1]))
print()
elif (device == 5): if (device == 4):
self._last_cnt[5] = last_data_cnt self._last_cnt[4] = last_data_cnt
elif (device == 6): elif (device == 5):
self._last_cnt[6] = last_data_cnt self._last_cnt[5] = last_data_cnt
elif (device == 7): elif (device == 6):
self._last_cnt[7] = last_data_cnt self._last_cnt[6] = last_data_cnt
elif (device == 7):
self._last_cnt[7] = last_data_cnt
return save return save
def _neu_foreach_data_section(self, data: bytes): def _neu_foreach_data_section(self, data: bytes):
@@ -514,59 +497,50 @@ class RecordingProcess(Process):
:return: :return:
""" """
raw_data = [] raw_data = []
records = 0 index = 0
print_flag = False elite_data_len = 40
mem_header_len = 3
mem_tailer_len = 8
single_data_len = elite_data_len + mem_header_len + mem_tailer_len
device = self._device device = self._device
single_data_len = self._single_data_len print_flag = False
mem_header_len = self._mem_header_len
# packet section data use two microarray # packet section data use two microarray
for i in range(0, len(data), single_data_len): for i in range(0, len(data), single_data_len):
records = records + 1 index = index + 1
raw_data.append(data[i : i + single_data_len]) for j in range(index, index + 1):
if len(data[i : i + 10]) < 10: raw_data.append(data[i : i + single_data_len])
print('data dont be package:', raw_data[records - 1], 'index:', records - 1)
del raw_data[records - 1]
records = records - 1
# check cnt # check cnt
for i in range(records): for i in range(index):
# if jump cnt, don't save data # if jump cnt, don't save data
save = self._check_cnt(raw_data[i], device) save = self._check_cnt(raw_data[i], device)
# # if jump Ram, also can handle data # # if jump Ram, also can handle data
if save == False: if save == False:
save = self._check_jump_ram(raw_data, i, records, save, device, single_data_len) save = self._check_jump_ram(raw_data, i, index, save, device)
if (save == True): if (save == True):
section = raw_data[i] section = raw_data[i]
check_sum = sum(section[0 : single_data_len - 1]) & 0b11111111 if (section[0 : mem_header_len] == section[single_data_len - 3: single_data_len]):
if (check_sum != section[single_data_len - 1]):
print('check number not match,',
list(section[0 : mem_header_len]), section[-1], '!=', check_sum, 'device:', device, ',', datetime.now())
if (i-1 >= 0):
print('check number not match', list(raw_data[i-1]), 'raw:', i-1)
print('check number not match', list(raw_data[i]), 'raw:', i)
if (i+1 <= records):
print('check number not match', list(raw_data[i+1]), 'raw:', i+1, '\n')
section = section + b'\x01'
print('delete data:', list(raw_data[i][0:3]), 'raw:', i, '\n')
continue
else:
section = section + b'\x00' section = section + b'\x00'
# section = section[3:] else:
section = section + b'\x01'
print('header != tailer,',
list(section[0 : mem_header_len]),
list(section[single_data_len - 3: single_data_len]),
'device:', device, ',', datetime.now())
print('header != tailer', list(section), '\n')
section = section[3:]
yield None, section yield None, section
else: else:
print_flag = True print_flag = True
print('delete data:', list(raw_data[i][0:3]), 'raw:', i, '\n') print('delete data:', list(raw_data[i]))
# if print_flag: # if print_flag:
# print('delete data context:') # print('delete data context:')
@@ -608,7 +582,7 @@ class RecordingProcess(Process):
def final_write(self): def final_write(self):
if self._writer is not None: if self._writer is not None:
return self._writer.close(self._mqtt_send_data_ch_level) return self._writer.close()
else: else:
return False return False
-15
View File
@@ -3,7 +3,6 @@ import inspect
import os import os
import sys import sys
import threading as _threading import threading as _threading
from time import time
from datetime import datetime from datetime import datetime
from functools import wraps from functools import wraps
from typing import Any, Optional, Tuple, Union from typing import Any, Optional, Tuple, Union
@@ -521,17 +520,3 @@ def logging_error(f):
return f(self, *args, **kwargs) return f(self, *args, **kwargs)
return _logger return _logger
def calculate_time(time_limit=None):
def actual_calculate_time(func):
def warp(*args, **kwargs):
now = time()
func(*args, **kwargs)
if time_limit == None:
print(func.__name__,'cost:', time() - now)
else:
if time() - now > time_limit:
print(func.__name__,'cost:', time() - now)
return warp
return actual_calculate_time
+5 -7
View File
@@ -93,15 +93,13 @@ class SocketClient(metaclass=abc.ABCMeta):
if self._socket is None: if self._socket is None:
try: try:
self._socket = Socket(AF_UNIX, SOCK_STREAM) self._socket = Socket(AF_UNIX, SOCK_STREAM)
self._socket.settimeout(5) self._socket.settimeout(1)
self._socket.connect(self._socket_file) self._socket.connect(self._socket_file)
except Exception as e: except FileNotFoundError as e:
print('open_socket error:', e) raise RuntimeError('server not available') from e
# except FileNotFoundError as e:
# raise RuntimeError('server not available') from e
# except ConnectionRefusedError as e: except ConnectionRefusedError as e:
# raise RuntimeError('server crash') from e raise RuntimeError('server crash') from e
def close_socket(self): def close_socket(self):
if self._socket is not None: if self._socket is not None:
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,655 +0,0 @@
{
"name": "Elite_EIS_1.0",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite.*",
"major_product_number": 0,
"minor_product_number": 4,
"major_version_number": 1,
"minor_version_number": 0
},
"constant": {
"FREQ_MAX": 13333334,
"VOLT_MAX": 65536,
"BLE_WRITE_MAX": 255,
"TIME_MAX": 100000
},
"parameters": {
"TIME_DURATION": {
"description": "Run duration",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"HIGHZ_TABLE": {
"initial": [
1,
1,
1,
0,
1,
1,
1,
1
],
"domain": {
"list": [
100
]
},
"value": "VALUE"
},
"CTRL_HIGH_Z_15": {
"description": "ctrl highZ level",
"record_meta": true,
"initial": 1,
"value": [
"On",
"Off"
],
"on_change": "set_ctrl_highZ"
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"initial": 1,
"domain": [
65536
],
"value": {
"expression": "VALUE"
}
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"CHANNEL": {
"derzteription": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"EIS CURVE",
"Cyclic Voltammetry",
"Chronoamperometric",
"V-T Graph",
"R-T Graph",
"EIS constant frequency",
"Dev Mode",
"Idle"
]
},
"GENERAL_HS_RTIA": {
"description": "High speed rtia gain",
"record_meta": true,
"initial": 8,
"value": [
0,
1,
2,
3,
4,
5,
6,
7,
8
],
"on_change": "set_general_hs_rtia"
},
"GENERAL_LP_RTIA": {
"description": "Low power rtia gain",
"record_meta": true,
"initial": 4,
"value": [
"0",
"1",
"2",
"3",
"auto"
],
"on_change": "set_general_lp_rtia"
},
"EIS_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"EIS_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"EIS_FREQ": {
"description": "[start, stop] of frequency scan",
"record_meta": true,
"initial": [
13333333,
7
],
"domain": {
"list": [
"FREQ_MAX"
]
},
"value": "VALUE"
},
"EIS_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"EIS_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"EIS_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"EIS_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CF_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"CF_FREQ": {
"description": "frequency of cf",
"record_meta": true,
"initial": 13333333,
"domain": [
"FREQ_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"CF_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"CF_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"CF_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CV_E_INITIAL": {
"description": "Initial Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E1": {
"description": "First voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E2": {
"description": "Second voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_SCAN_RATE": {
"description": "Voltage scan rate",
"record_meta": true,
"initial": 10000,
"domain": [
100001
],
"value": {
"expression": "VALUE"
}
},
"CA_VOLT": {
"description": "Voltage of ca",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"VT_MEASURE_VIN_RANGE": {
"description": "measure range of Vin",
"record_meta": true,
"initial": 0,
"value": [
"0",
"1",
"2",
"auto"
]
},
"RT_VOLT_SET": {
"description": "DAC output Voltage",
"record_meta": true,
"initial": 37500,
"domain": [
65536
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_RT_VOLT_SET"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"7": "idle",
"*": "start_data"
}
}
],
"idle": [
"_idle()"
],
"start_data": [
"data_format_cali",
"_notify(True)",
{
"expression": "MODE",
"when": {
"0": "set_general_hs_rtia",
"1": "set_general_lp_rtia",
"2": "set_general_lp_rtia",
"3": "set_general_lp_rtia",
"4": "set_general_lp_rtia",
"5": "set_general_hs_rtia"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis",
"1": "curve_cv3",
"2": "curve_ca",
"3": "curve_vt",
"4": "curve_rt",
"5": "curve_cf"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_para2",
"1": "curve_cv3_para2",
"5": "curve_cf_para2"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_mode",
"5": "curve_cf_mode"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('EISZeroOne')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"data_format_cali": [
"_data_format_cali('EISZeroOne')"
],
"set_general_hs_rtia": {
"type": "RIS",
"data": [
"1XFF;1X70;1B>GENERAL_HS_RTIA"
]
},
"set_general_lp_rtia": {
"type": "RIS",
"data": [
"1XFF;1X71;1B>GENERAL_LP_RTIA"
]
},
"set_sample_rate": {
"type": "RIS",
"parameter": {
"va": "SAMPLE_RATE"
},
"data": [
"XE0;2B>va"
]
},
"set_ctrl_highZ": {
"type": "RIS",
"data": [
"1XFF;1X72;1B>CTRL_HIGH_Z_15"
]
},
"curve_eis": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X01;4B>fa;4B>fb;2B>dp"
]
},
"curve_eis_para2": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_eis_mode": {
"type": "RIS",
"data": [
"1X12;1XFF"
]
},
"curve_cf": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X01;4B>fa;4B>0;2B>dp"
]
},
"curve_cf_para2": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_cf_mode": {
"type": "RIS",
"data": [
"1X13;1XFF"
]
},
"curve_cv3": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X01;2B>va;2B>vb;2B>vc;"
]
},
"curve_cv3_para2": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X02;4B>ve;2B>vf;2B>cn"
]
},
"curve_ca": {
"type": "RIS",
"parameter": {
"va": "CA_VOLT",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"1X0B;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_rt": {
"type": "RIS",
"parameter": {
"va": "RT_VOLT_SET",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"X04;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_vt": {
"type": "RIS",
"parameter": {
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE",
"va": "VT_MEASURE_VIN_RANGE"
},
"data": [
"X05;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe;",
"1B>va"
]
},
"set_para_RT_VOLT_SET": {
"type": "RIS",
"data": [
"XE2;X01;2B>RT_VOLT_SET"
]
},
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "RIS",
"data": [
"XFF;",
"1B>BLE_WRITE[0];1B>BLE_WRITE[1];1B>BLE_WRITE[2];1B>BLE_WRITE[3];",
"1B>BLE_WRITE[4];1B>BLE_WRITE[5];1B>BLE_WRITE[6];1B>BLE_WRITE[7];",
"1B>BLE_WRITE[8];1B>BLE_WRITE[9];1B>BLE_WRITE[10];1B>BLE_WRITE[11];",
"1B>BLE_WRITE[12];1B>BLE_WRITE[13];1B>BLE_WRITE[14];1B>BLE_WRITE[15];",
"1B>BLE_WRITE[16];"
]
},
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
]
}
}
@@ -1,655 +0,0 @@
{
"name": "Elite_EIS_1.1",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite.*",
"major_product_number": 0,
"minor_product_number": 4,
"major_version_number": 1,
"minor_version_number": 1
},
"constant": {
"FREQ_MAX": 13333334,
"VOLT_MAX": 65536,
"BLE_WRITE_MAX": 255,
"TIME_MAX": 100000
},
"parameters": {
"TIME_DURATION": {
"description": "Run duration",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"HIGHZ_TABLE": {
"initial": [
1,
1,
1,
0,
1,
1,
1,
1
],
"domain": {
"list": [
100
]
},
"value": "VALUE"
},
"CTRL_HIGH_Z_15": {
"description": "ctrl highZ level",
"record_meta": true,
"initial": 1,
"value": [
"On",
"Off"
],
"on_change": "set_ctrl_highZ"
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"initial": 1,
"domain": [
65536
],
"value": {
"expression": "VALUE"
}
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"CHANNEL": {
"derzteription": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"EIS CURVE",
"Cyclic Voltammetry",
"Chronoamperometric",
"V-T Graph",
"R-T Graph",
"EIS constant frequency",
"Dev Mode",
"Idle"
]
},
"GENERAL_HS_RTIA": {
"description": "High speed rtia gain",
"record_meta": true,
"initial": 8,
"value": [
0,
1,
2,
3,
4,
5,
6,
7,
8
],
"on_change": "set_general_hs_rtia"
},
"GENERAL_LP_RTIA": {
"description": "Low power rtia gain",
"record_meta": true,
"initial": 4,
"value": [
"0",
"1",
"2",
"3",
"auto"
],
"on_change": "set_general_lp_rtia"
},
"EIS_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"EIS_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"EIS_FREQ": {
"description": "[start, stop] of frequency scan",
"record_meta": true,
"initial": [
13333333,
7
],
"domain": {
"list": [
"FREQ_MAX"
]
},
"value": "VALUE"
},
"EIS_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"EIS_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"EIS_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"EIS_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CF_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"CF_FREQ": {
"description": "frequency of cf",
"record_meta": true,
"initial": 13333333,
"domain": [
"FREQ_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"CF_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"CF_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"CF_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CV_E_INITIAL": {
"description": "Initial Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E1": {
"description": "First voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E2": {
"description": "Second voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_SCAN_RATE": {
"description": "Voltage scan rate",
"record_meta": true,
"initial": 10000,
"domain": [
100001
],
"value": {
"expression": "VALUE"
}
},
"CA_VOLT": {
"description": "Voltage of ca",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"VT_MEASURE_VIN_RANGE": {
"description": "measure range of Vin",
"record_meta": true,
"initial": 0,
"value": [
"0",
"1",
"2",
"auto"
]
},
"RT_VOLT_SET": {
"description": "DAC output Voltage",
"record_meta": true,
"initial": 37500,
"domain": [
65536
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_RT_VOLT_SET"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"7": "idle",
"*": "start_data"
}
}
],
"idle": [
"_idle()"
],
"start_data": [
"data_format_cali",
"_notify(True)",
{
"expression": "MODE",
"when": {
"0": "set_general_hs_rtia",
"1": "set_general_lp_rtia",
"2": "set_general_lp_rtia",
"3": "set_general_lp_rtia",
"4": "set_general_lp_rtia",
"5": "set_general_hs_rtia"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis",
"1": "curve_cv3",
"2": "curve_ca",
"3": "curve_vt",
"4": "curve_rt",
"5": "curve_cf"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_para2",
"1": "curve_cv3_para2",
"5": "curve_cf_para2"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_mode",
"5": "curve_cf_mode"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('EISZeroOne')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"data_format_cali": [
"_data_format_cali('EISZeroOne')"
],
"set_general_hs_rtia": {
"type": "RIS",
"data": [
"1XFF;1X70;1B>GENERAL_HS_RTIA"
]
},
"set_general_lp_rtia": {
"type": "RIS",
"data": [
"1XFF;1X71;1B>GENERAL_LP_RTIA"
]
},
"set_sample_rate": {
"type": "RIS",
"parameter": {
"va": "SAMPLE_RATE"
},
"data": [
"XE0;2B>va"
]
},
"set_ctrl_highZ": {
"type": "RIS",
"data": [
"1XFF;1X72;1B>CTRL_HIGH_Z_15"
]
},
"curve_eis": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X01;4B>fa;4B>fb;2B>dp"
]
},
"curve_eis_para2": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_eis_mode": {
"type": "RIS",
"data": [
"1X12;1XFF"
]
},
"curve_cf": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X01;4B>fa;4B>0;2B>dp"
]
},
"curve_cf_para2": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_cf_mode": {
"type": "RIS",
"data": [
"1X13;1XFF"
]
},
"curve_cv3": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X01;2B>va;2B>vb;2B>vc;"
]
},
"curve_cv3_para2": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X02;4B>ve;2B>vf;2B>cn"
]
},
"curve_ca": {
"type": "RIS",
"parameter": {
"va": "CA_VOLT",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"1X0B;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_rt": {
"type": "RIS",
"parameter": {
"va": "RT_VOLT_SET",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"X04;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_vt": {
"type": "RIS",
"parameter": {
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE",
"va": "VT_MEASURE_VIN_RANGE"
},
"data": [
"X05;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe;",
"1B>va"
]
},
"set_para_RT_VOLT_SET": {
"type": "RIS",
"data": [
"XE2;X01;2B>RT_VOLT_SET"
]
},
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "RIS",
"data": [
"XFF;",
"1B>BLE_WRITE[0];1B>BLE_WRITE[1];1B>BLE_WRITE[2];1B>BLE_WRITE[3];",
"1B>BLE_WRITE[4];1B>BLE_WRITE[5];1B>BLE_WRITE[6];1B>BLE_WRITE[7];",
"1B>BLE_WRITE[8];1B>BLE_WRITE[9];1B>BLE_WRITE[10];1B>BLE_WRITE[11];",
"1B>BLE_WRITE[12];1B>BLE_WRITE[13];1B>BLE_WRITE[14];1B>BLE_WRITE[15];",
"1B>BLE_WRITE[16];"
]
},
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
]
}
}
@@ -1,655 +0,0 @@
{
"name": "Elite_EIS_MINI_1.0",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite.*",
"major_product_number": 0,
"minor_product_number": 4,
"major_version_number": 1,
"minor_version_number": 2
},
"constant": {
"FREQ_MAX": 13333334,
"VOLT_MAX": 65536,
"BLE_WRITE_MAX": 255,
"TIME_MAX": 100000
},
"parameters": {
"TIME_DURATION": {
"description": "Run duration",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"HIGHZ_TABLE": {
"initial": [
1,
1,
1,
0,
1,
1,
1,
1
],
"domain": {
"list": [
100
]
},
"value": "VALUE"
},
"CTRL_HIGH_Z_15": {
"description": "ctrl highZ level",
"record_meta": true,
"initial": 1,
"value": [
"On",
"Off"
],
"on_change": "set_ctrl_highZ"
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"initial": 1,
"domain": [
65536
],
"value": {
"expression": "VALUE"
}
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"CHANNEL": {
"derzteription": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"EIS CURVE",
"Cyclic Voltammetry",
"Chronoamperometric",
"V-T Graph",
"R-T Graph",
"EIS constant frequency",
"Dev Mode",
"Idle"
]
},
"GENERAL_HS_RTIA": {
"description": "High speed rtia gain",
"record_meta": true,
"initial": 8,
"value": [
0,
1,
2,
3,
4,
5,
6,
7,
8
],
"on_change": "set_general_hs_rtia"
},
"GENERAL_LP_RTIA": {
"description": "Low power rtia gain",
"record_meta": true,
"initial": 4,
"value": [
"0",
"1",
"2",
"3",
"auto"
],
"on_change": "set_general_lp_rtia"
},
"EIS_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"EIS_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"EIS_FREQ": {
"description": "[start, stop] of frequency scan",
"record_meta": true,
"initial": [
13333333,
7
],
"domain": {
"list": [
"FREQ_MAX"
]
},
"value": "VALUE"
},
"EIS_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"EIS_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"EIS_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"EIS_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CF_DC_BIAS": {
"description": "DC voltage bias",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_AC_AMP": {
"description": "AC Amplitude",
"record_meta": true,
"initial": 26,
"domain": [
2048
]
},
"CF_FREQ": {
"description": "frequency of cf",
"record_meta": true,
"initial": 13333333,
"domain": [
"FREQ_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CF_PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"CF_SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"CF_DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"CF_AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
6,
8
]
},
"CV_E_INITIAL": {
"description": "Initial Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E1": {
"description": "First voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_E2": {
"description": "Second voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CV_SCAN_RATE": {
"description": "Voltage scan rate",
"record_meta": true,
"initial": 10000,
"domain": [
100001
],
"value": {
"expression": "VALUE"
}
},
"CA_VOLT": {
"description": "Voltage of ca",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"VT_MEASURE_VIN_RANGE": {
"description": "measure range of Vin",
"record_meta": true,
"initial": 0,
"value": [
"0",
"1",
"2",
"auto"
]
},
"RT_VOLT_SET": {
"description": "DAC output Voltage",
"record_meta": true,
"initial": 37500,
"domain": [
65536
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_RT_VOLT_SET"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"7": "idle",
"*": "start_data"
}
}
],
"idle": [
"_idle()"
],
"start_data": [
"data_format_cali",
"_notify(True)",
{
"expression": "MODE",
"when": {
"0": "set_general_hs_rtia",
"1": "set_general_lp_rtia",
"2": "set_general_lp_rtia",
"3": "set_general_lp_rtia",
"4": "set_general_lp_rtia",
"5": "set_general_hs_rtia"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis",
"1": "curve_cv3",
"2": "curve_ca",
"3": "curve_vt",
"4": "curve_rt",
"5": "curve_cf"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_para2",
"1": "curve_cv3_para2",
"5": "curve_cf_para2"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_mode",
"5": "curve_cf_mode"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('EISZeroOne')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"data_format_cali": [
"_data_format_cali('EISZeroOne')"
],
"set_general_hs_rtia": {
"type": "RIS",
"data": [
"1XFF;1X70;1B>GENERAL_HS_RTIA"
]
},
"set_general_lp_rtia": {
"type": "RIS",
"data": [
"1XFF;1X71;1B>GENERAL_LP_RTIA"
]
},
"set_sample_rate": {
"type": "RIS",
"parameter": {
"va": "SAMPLE_RATE"
},
"data": [
"XE0;2B>va"
]
},
"set_ctrl_highZ": {
"type": "RIS",
"data": [
"1XFF;1X72;1B>CTRL_HIGH_Z_15"
]
},
"curve_eis": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X01;4B>fa;4B>fb;2B>dp"
]
},
"curve_eis_para2": {
"type": "RIS",
"parameter": {
"fa": "EIS_FREQ[0]",
"fb": "EIS_FREQ[1]",
"dp": "EIS_DELAY",
"dc": "EIS_DC_BIAS",
"am": "EIS_AC_AMP",
"an": "EIS_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "EIS_PPD",
"sp": "EIS_SCALE"
},
"data": [
"1X12;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_eis_mode": {
"type": "RIS",
"data": [
"1X12;1XFF"
]
},
"curve_cf": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X01;4B>fa;4B>0;2B>dp"
]
},
"curve_cf_para2": {
"type": "RIS",
"parameter": {
"fa": "CF_FREQ",
"dp": "CF_DELAY",
"dc": "CF_DC_BIAS",
"am": "CF_AC_AMP",
"an": "CF_AVERAGE_NUM",
"rt": "GENERAL_HS_RTIA",
"pp": "CF_PPD",
"sp": "CF_SCALE"
},
"data": [
"1X13;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_cf_mode": {
"type": "RIS",
"data": [
"1X13;1XFF"
]
},
"curve_cv3": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X01;2B>va;2B>vb;2B>vc;"
]
},
"curve_cv3_para2": {
"type": "RIS",
"parameter": {
"va": "CV_E_INITIAL",
"vb": "CV_E1",
"vc": "CV_E2",
"ve": "CV_SCAN_RATE",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1X09;1X02;4B>ve;2B>vf;2B>cn"
]
},
"curve_ca": {
"type": "RIS",
"parameter": {
"va": "CA_VOLT",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"1X0B;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_rt": {
"type": "RIS",
"parameter": {
"va": "RT_VOLT_SET",
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE"
},
"data": [
"X04;",
"2B>va;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe"
]
},
"curve_vt": {
"type": "RIS",
"parameter": {
"pa": "GENERAL_LP_RTIA",
"pd": "CTRL_HIGH_Z_15",
"pe": "SAMPLE_RATE",
"va": "VT_MEASURE_VIN_RANGE"
},
"data": [
"X05;",
"4b>pa;4b>0;",
"4b>0;4b>pd;",
"2B>pe;",
"1B>va"
]
},
"set_para_RT_VOLT_SET": {
"type": "RIS",
"data": [
"XE2;X01;2B>RT_VOLT_SET"
]
},
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "RIS",
"data": [
"XFF;",
"1B>BLE_WRITE[0];1B>BLE_WRITE[1];1B>BLE_WRITE[2];1B>BLE_WRITE[3];",
"1B>BLE_WRITE[4];1B>BLE_WRITE[5];1B>BLE_WRITE[6];1B>BLE_WRITE[7];",
"1B>BLE_WRITE[8];1B>BLE_WRITE[9];1B>BLE_WRITE[10];1B>BLE_WRITE[11];",
"1B>BLE_WRITE[12];1B>BLE_WRITE[13];1B>BLE_WRITE[14];1B>BLE_WRITE[15];",
"1B>BLE_WRITE[16];"
]
},
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
]
}
}
File diff suppressed because it is too large Load Diff
+221
View File
@@ -0,0 +1,221 @@
{
"name": "Elite",
"version": "0.1",
"match_rule": {
"local_name_pattern": "Elite.*",
"major_product_number": 0,
"minor_product_number": 1,
"major_version_number": 0,
"minor_version_number": 1
},
"constant": {
"REC_CHANNEL_COUNT": 16,
"STI_CHANNEL_COUNT": 15,
"ADC_SAMPLE_RATE_LIST": [
15,
25,
40,
50,
100,
250,
500,
1000,
2500,
4000,
5000
],
"STI_FREQ_LIST": [
15,
25,
40,
50,
100,
250,
500,
1000,
2500,
4000,
5000
]
},
"parameters": {
"MODE": {
"description": "working mode",
"value": [
"Recording Mode",
"Stimulation Mode",
"Artifact Mode"
]
},
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": {
"set": [
"REC_CHANNEL_COUNT"
]
}
},
"SAMPLE_RATE": {
"alias": "ADC_RATE",
"description": "sampling rate",
"record_meta": true,
"domain": [
"len(ADC_SAMPLE_RATE_LIST)"
],
"value": "ADC_SAMPLE_RATE_LIST[VALUE]"
},
"AMP_GAIN": {
"description": "",
"record_meta": true,
"value": [
400,
1000,
2000
]
},
"LOW_FREQ_BAND": {
"description": "",
"record_meta": true,
"domain": [
7
]
},
"STI_MODE": {
"description": "stimulation mode",
"value": [
"Positive Pulse",
"Negative Pulse",
"Pos to Neg Pulse",
"Neg to Pos Pulse",
"Arbitrary Waveform"
]
},
"STI_VOLT": {
"description": "stimulation voltage",
"domain": [
32
],
"value": "2 + 3 * VALUE / 31"
},
"STI_FREQ": {
"description": "stimulation frequency",
"domain": [
"len(STI_FREQ_LIST)"
],
"value": "STI_FREQ_LIST[VALUE]"
},
"STI_PMOS": {
"description": "stimulation PMOS channel",
"initial": 0,
"domain": [
"STI_CHANNEL_COUNT"
],
"guard": "STI_PMOS != STI_NMOS"
},
"STI_NMOS": {
"description": "stimulation NMOS channel",
"initial": "STI_CHANNEL_COUNT - 1",
"domain": [
"STI_CHANNEL_COUNT"
],
"guard": "STI_PMOS != STI_NMOS"
},
"CHOPPER": {
"description": "",
"domain": "bool"
},
"FAST_SETTLE": {
"description": "",
"domain": "bool"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"0": "start_recording",
"1": "start_stimulation",
"2": "start_artifact"
}
},
"VIS_STI"
],
"start_recording": [
{
"expression": "len(CHANNEL) > 0",
"raise": "no recording channel"
},
"data_format",
"_notify(True)",
"start_recording0",
"_sync(True)"
],
"start_artifact": [
{
"expression": "len(CHANNEL) > 0",
"raise": "no recording channel"
},
"data_format",
"_notify(True)",
"start_artifact0",
"_sync(True)"
],
"data_format": [
"_data_format('TDC4VAF2')"
],
"start_recording0": {
"type": "RIS",
"foreach-parameter": {
"parameter": "CHANNEL",
"variable": [
"a",
"b",
"c",
"d"
]
},
"data": [
"3b110#recording header;2b<AMP_GAIN;3b<LOW_FREQ_BAND",
"4b>SAMPLE_RATE;1ba?;1bb?;1bc?;1bd?",
"4b<a?:0;4b<b?:0",
"4b<c?:0;4b<d?:0"
]
},
"start_artifact0": {
"type": "RIS",
"foreach-parameter": {
"parameter": "CHANNEL",
"variable": [
"a",
"b",
"c",
"d"
]
},
"data": [
"3b100#artifact mode header;2b0;3b001",
"4b>SAMPLE_RATE;1ba?;1bb?;1bc?;1bd?",
"4b<a?:0;4b<b?:0",
"4b<c?:0;4b<d?:0"
]
},
"start_stimulation": [
"start_stimulation0"
],
"start_stimulation0": {
"type": "RIS",
"guard": {
"expression": "STI_PMOS != STI_NMOS",
"raise": "same N/PMos channel"
},
"data": [
"3b111#stimulation header;2b0;3bSTI_MODE",
"4b>STI_FREQ;1bCHOPPER;1bFAST_SETTLE;2b0",
"4b<STI_PMOS;4b<STI_NMOS",
"5b<STI_VOLT;3b0"
]
}
}
}
@@ -0,0 +1,433 @@
{
"name": "EliteEIS",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite.*",
"major_product_number": 0,
"minor_product_number": 4,
"major_version_number": 1,
"minor_version_number": 0
},
"constant": {
"FREQ_MAX": 4294967296,
"VOLT_MAX": 65536,
"BLE_WRITE_MAX": 255
},
"parameters": {
"CHANNEL": {
"derzteription": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "channel label",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"EIS CURVE",
"Cyclic Voltammetry",
"Dev Mode"
]
},
"FREQ_START": {
"description": "Start of Freq Scan",
"record_meta": true,
"initial": 13422819,
"domain": [
"FREQ_MAX"
],
"value": {
"expression": "VALUE"
}
},
"FREQ_STOP": {
"description": "End of Freq Scan",
"record_meta": true,
"initial": 7,
"domain": [
"FREQ_MAX"
],
"value": {
"expression": "VALUE"
}
},
"DELAY": {
"description": "Wait x peroid before start taking measurements",
"record_meta": true,
"initial": 0,
"domain": [
101
],
"value": {
"expression": "VALUE"
}
},
"AVERAGE_NUM": {
"description": "Number of sample used for average funciton",
"record_meta": true,
"initial": 8,
"value": [
2,
4,
8,
16
]
},
"DC_BIAS": {
"description": "DC voltage bias in mV",
"record_meta": true,
"initial": 15000,
"domain": [
35001
],
"value": {
"expression": "VALUE"
}
},
"AC_AMP": {
"description": "AC Amplitude in mV",
"record_meta": true,
"initial": 25,
"domain": [
2048
]
},
"SCALE": {
"description": "Point spacing pattern",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"PPD": {
"description": "Point per decades",
"record_meta": true,
"initial": 10,
"domain": [
11
]
},
"RTIA": {
"description": "High speed tia gain",
"record_meta": true,
"initial": 4,
"value": [
0,
1,
2,
3,
4
]
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"Scan_Rate": {
"description": "Voltage Scan Rate",
"record_meta": true,
"initial": 10000,
"domain": [
100001
],
"value": {
"expression": "VALUE"
}
},
"CTRL_HIGH_Z_15": {
"description": "ctrl highZ level",
"record_meta": true,
"initial": 1,
"value": [
"On",
"Off"
],
"on_change": "set_ctrl_highZ"
},
"ADC_LEVEL_I_15": {
"description": "ADC level",
"record_meta": true,
"initial": 4,
"value": [
"0",
"1",
"2",
"3",
"auto"
],
"on_change": "set_adc_gain_I"
},
"ADC_LEVEL_V_IN_15": {
"description": "ADC level",
"record_meta": true,
"initial": 3,
"value": [
"0",
"1",
"2",
"auto"
],
"on_change": "set_adc_gain_Vin"
},
"VOLT_INITIAL": {
"description": "Initial Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"VOLT_MAX": {
"description": "Max Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"VOLT_MIN": {
"description": "Min Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "VALUE"
}
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"initial": 1,
"domain": [
65536
],
"value": {
"expression": "VALUE"
}
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"*": "start_data"
}
}
],
"start_data": [
"data_format_cali",
"_notify(True)",
{
"expression": "MODE",
"when": {
"1": "set_adc_gain_I"
}
},
{
"expression": "MODE",
"when": {
"1": "set_adc_gain_Vin"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis",
"1": "curve_cv3"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_eis_para2",
"1": "curve_cv3_para2"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('EISZeroOne')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"data_format_cali": [
"_data_format_cali('EISZeroOne')"
],
"set_sample_rate": {
"type": "RIS",
"parameter": {
"va": "SAMPLE_RATE"
},
"data": [
"XE0;2B>va"
]
},
"set_adc_gain_I": {
"type": "RIS",
"data": [
"XE1;X05;B>ADC_LEVEL_I_15"
]
},
"set_adc_gain_Vin": {
"type": "RIS",
"data": [
"XE1;X06;B>ADC_LEVEL_V_IN_15"
]
},
"set_ctrl_highZ": {
"type": "RIS",
"data": [
"XE1;X03;B>CTRL_HIGH_Z_15"
]
},
"curve_eis": {
"type": "RIS",
"parameter": {
"fa": "FREQ_START",
"fb": "FREQ_STOP",
"dp": "DELAY",
"dc": "DC_BIAS",
"am": "AC_AMP",
"an": "AVERAGE_NUM",
"rt": "RTIA",
"pp": "PPD",
"sp": "SCALE"
},
"data": [
"1XD1;1X01;4B>fa;4B>fb;2B>dp"
]
},
"curve_eis_para2": {
"type": "RIS",
"parameter": {
"fa": "FREQ_START",
"fb": "FREQ_STOP",
"dp": "DELAY",
"dc": "DC_BIAS",
"am": "AC_AMP",
"an": "AVERAGE_NUM",
"rt": "RTIA",
"pp": "PPD",
"sp": "SCALE"
},
"data": [
"1XD1;1X02;2B>dc;2B>am;B>an;B>rt;2B>pp;B>sp"
]
},
"curve_cv3": {
"type": "RIS",
"parameter": {
"va": "VOLT_INITIAL",
"vb": "VOLT_MAX",
"vc": "VOLT_MIN",
"ve": "Scan_Rate",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1XD2;1X01;2B>va;2B>vb;2B>vc;"
]
},
"curve_cv3_para2": {
"type": "RIS",
"parameter": {
"va": "VOLT_INITIAL",
"vb": "VOLT_MAX",
"vc": "VOLT_MIN",
"ve": "Scan_Rate",
"vf": "SAMPLE_RATE",
"cn": "CYCLE_NUMBER"
},
"data": [
"1XD2;1X02;4B>ve;2B>vf;2B>cn"
]
},
"VIS_CC_ZERO": [
"_data_format('EISZeroOne')",
"_disable_cache(False)",
"_notify(True)",
"VIS_CC_ZERO",
"_sync(True)",
"VIS_STI"
],
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "RIS",
"data": [
"XFF;",
"1B>BLE_WRITE[0];1B>BLE_WRITE[1];1B>BLE_WRITE[2];1B>BLE_WRITE[3];",
"1B>BLE_WRITE[4];1B>BLE_WRITE[5];1B>BLE_WRITE[6];1B>BLE_WRITE[7];",
"1B>BLE_WRITE[8];1B>BLE_WRITE[9];1B>BLE_WRITE[10];1B>BLE_WRITE[11];",
"1B>BLE_WRITE[12];1B>BLE_WRITE[13];1B>BLE_WRITE[14];1B>BLE_WRITE[15];",
"1B>BLE_WRITE[16];"
]
},
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
]
}
}
@@ -0,0 +1,220 @@
{
"name": "Elite_Legacy",
"version": "0.0.0",
"match_rule": {
"major_product_number": 0,
"minor_product_number": 0,
"major_version_number": 0,
"minor_version_number": 0
},
"constant": {
"REC_CHANNEL_COUNT": 16,
"STI_CHANNEL_COUNT": 15,
"ADC_SAMPLE_RATE_LIST": [
15,
25,
40,
50,
100,
250,
500,
1000,
2500,
4000,
5000
],
"STI_FREQ_LIST": [
15,
25,
40,
50,
100,
250,
500,
1000,
2500,
4000,
5000
]
},
"parameters": {
"MODE": {
"description": "working mode",
"value": [
"Recording Mode",
"Stimulation Mode",
"Artifact Mode"
]
},
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": {
"set": [
"REC_CHANNEL_COUNT"
]
}
},
"SAMPLE_RATE": {
"alias": "ADC_RATE",
"description": "sampling rate",
"record_meta": true,
"domain": [
"len(ADC_SAMPLE_RATE_LIST)"
],
"value": "ADC_SAMPLE_RATE_LIST[VALUE]"
},
"AMP_GAIN": {
"description": "",
"record_meta": true,
"value": [
400,
1000,
2000
]
},
"LOW_FREQ_BAND": {
"description": "",
"record_meta": true,
"domain": [
7
]
},
"STI_MODE": {
"description": "stimulation mode",
"value": [
"Positive Pulse",
"Negative Pulse",
"Pos to Neg Pulse",
"Neg to Pos Pulse",
"Arbitrary Waveform"
]
},
"STI_VOLT": {
"description": "stimulation voltage",
"domain": [
32
],
"value": "2 + 3 * VALUE / 31"
},
"STI_FREQ": {
"description": "stimulation frequency",
"domain": [
"len(STI_FREQ_LIST)"
],
"value": "STI_FREQ_LIST[VALUE]"
},
"STI_PMOS": {
"description": "stimulation PMOS channel",
"initial": 0,
"domain": [
"STI_CHANNEL_COUNT"
],
"guard": "STI_PMOS != STI_NMOS"
},
"STI_NMOS": {
"description": "stimulation NMOS channel",
"initial": "STI_CHANNEL_COUNT - 1",
"domain": [
"STI_CHANNEL_COUNT"
],
"guard": "STI_PMOS != STI_NMOS"
},
"CHOPPER": {
"description": "",
"domain": "bool"
},
"FAST_SETTLE": {
"description": "",
"domain": "bool"
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"0": "start_recording",
"1": "start_stimulation",
"2": "start_artifact"
}
},
"VIS_STI"
],
"start_recording": [
{
"expression": "len(CHANNEL) > 0",
"raise": "no recording channel"
},
"data_format",
"_notify(True)",
"start_recording0",
"_sync(True)"
],
"start_artifact": [
{
"expression": "len(CHANNEL) > 0",
"raise": "no recording channel"
},
"data_format",
"_notify(True)",
"start_artifact0",
"_sync(True)"
],
"data_format": [
"_data_format('TC4VAF2')"
],
"start_recording0": {
"type": "RIS",
"foreach-parameter": {
"parameter": "CHANNEL",
"variable": [
"a",
"b",
"c",
"d"
]
},
"data": [
"3b110#recording header;2b<AMP_GAIN;3b<LOW_FREQ_BAND",
"4b>SAMPLE_RATE;1ba?;1bb?;1bc?;1bd?",
"4b<a?:0;4b<b?:0",
"4b<c?:0;4b<d?:0"
]
},
"start_artifact0": {
"type": "RIS",
"foreach-parameter": {
"parameter": "CHANNEL",
"variable": [
"a",
"b",
"c",
"d"
]
},
"data": [
"3b100#artifact mode header;2b0;3b001",
"4b>SAMPLE_RATE;1ba?;1bb?;1bc?;1bd?",
"4b<a?:0;4b<b?:0",
"4b<c?:0;4b<d?:0"
]
},
"start_stimulation": [
"start_stimulation0"
],
"start_stimulation0": {
"type": "RIS",
"guard": {
"expression": "STI_PMOS != STI_NMOS",
"raise": "same N/PMos channel"
},
"data": [
"3b111#stimulation header;2b0;3bSTI_MODE",
"4b>STI_FREQ;1bCHOPPER;1bFAST_SETTLE;2b0",
"4b<STI_PMOS;4b<STI_NMOS",
"5b<STI_VOLT;3b0"
]
}
}
}
@@ -0,0 +1,300 @@
{
"name": "Elite_TRI",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Trigger.*",
"major_product_number": 0,
"minor_product_number": 5,
"major_version_number": 1,
"minor_version_number": 0
},
"constant": {
},
"parameters": {
"ACC_a_out0": {
"description": "Switch of analog current channel 1",
"record_meta": true,
"initial": 0,
"value": [
0,
1
],
"on_change": "set_para_AOUT0"
},
"ACC_a_out0_current": {
"description": "current value of analog current channel 1",
"record_meta": true,
"initial": 0,
"domain": [
50001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_AOUT0"
},
"ACC_a_out1": {
"description": "Switch of analog current channel 2",
"record_meta": true,
"initial": 0,
"value": [
0,
1
],
"on_change": "set_para_AOUT1"
},
"ACC_a_out1_current": {
"description": "current value of analog current channel 2",
"record_meta": true,
"initial": 0,
"domain": [
50001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_AOUT1"
},
"ACC_a_out2": {
"description": "Switch of analog current channel 3",
"record_meta": true,
"initial": 0,
"value": [
0,
1
],
"on_change": "set_para_AOUT2"
},
"ACC_a_out2_current": {
"description": "current value of analog current channel 3",
"record_meta": true,
"initial": 0,
"domain": [
50001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_AOUT2"
},
"ACC_a_out3": {
"description": "Switch of current channel 4",
"record_meta": true,
"initial": 0,
"value": [
0,
1
],
"on_change": "set_para_AOUT3"
},
"ACC_a_out3_current": {
"description": "current value of analog current channel 4",
"record_meta": true,
"initial":0,
"domain": [
50001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_para_AOUT3"
},
"TRIG0_en": {
"description": "Switch of current channel 4",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"TRIG1_en": {
"description": "Switch of current channel 4",
"record_meta": true,
"initial": 0,
"value": [
0,
1
]
},
"TRIG0_edge_type": {
"description": "Switch of current channel 4",
"record_meta": true,
"initial": 0,
"value": [
"RISING",
"FALLING",
"BOTH"
]
},
"TRIG1_edge_type": {
"description": "Switch of current channel 4",
"record_meta": true,
"initial": 0,
"value": [
"RISING",
"FALLING",
"BOTH"
]
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"Analog Current Control (ACC)"
]
},
"CHANNEL": {
"description": "delete it",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "delete it",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "delete it",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "delete it",
"record_meta": true,
"domain": "constant",
"value": 1
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"*": "start_data"
}
}
],
"start_data": [
"data_format",
"_notify(True)",
{
"expression": "MODE",
"when": {
"0": "set_aout_chan_en"
}
},
{
"expression": "MODE",
"when": {
"0": "set_TRIG_EN"
}
},
{
"expression": "MODE",
"when": {
"0": "curve_acc"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('I4V4Z4T4')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
],
"curve_acc": {
"type": "RIS",
"parameter": {
"va": "ACC_a_out0_current",
"vb": "ACC_a_out1_current",
"vc": "ACC_a_out2_current",
"vd": "ACC_a_out3_current"
},
"data": [
"X0E;",
"2B>va;2B>vb;2B>vc;2B>vd"
]
},
"set_aout_chan_en": {
"type": "RIS",
"parameter": {
"va": "ACC_a_out0",
"vb": "ACC_a_out1",
"vc": "ACC_a_out2",
"vd": "ACC_a_out3"
},
"data": [
"X81;X02;",
"4b>va;4b>vb;4b>vc;4b>vd"
]
},
"set_TRIG_EN": {
"type": "RIS",
"parameter": {
"va": "TRIG0_en",
"vb": "TRIG1_en",
"vc": "TRIG0_edge_type",
"vd": "TRIG1_edge_type"
},
"data": [
"X41;",
"4b>va;4b>vb;4b>vc;4b>vd"
]
},
"set_para_AOUT0": {
"type": "RIS",
"data": [
"XE2;X02;X00;2B>ACC_a_out0_current;1B>ACC_a_out0"
]
},
"set_para_AOUT1": {
"type": "RIS",
"data": [
"XE2;X02;X01;2B>ACC_a_out1_current;1B>ACC_a_out1"
]
},
"set_para_AOUT2": {
"type": "RIS",
"data": [
"XE2;X02;X02;2B>ACC_a_out2_current;1B>ACC_a_out2"
]
},
"set_para_AOUT3": {
"type": "RIS",
"data": [
"XE2;X02;X03;2B>ACC_a_out3_current;1B>ACC_a_out3"
]
}
}
}
+343
View File
@@ -0,0 +1,343 @@
{
"name": "EliteZM",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-ZM.+",
"major_product_number": 0,
"minor_product_number": 2,
"major_version_number": 1,
"minor_version_number": 2
},
"constant": {
"ADC_CHANNEL_NUMBER": [
12,
13,
14,
15
],
"VOLT_MAX": 65536,
"STEP_MAX": 100,
"Const_Current_Range": 1500001
},
"parameters": {
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "channel label",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"value": [
100,
10,
5,
1,
0.1,
0.01
],
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"I-V Curve",
"Cyclic Voltammetry",
"Function Generator",
"R-T Curve",
"V-T Curve",
"I-T Curve",
"ADC test",
"Constant Current"
]
},
"VOLT_ORIGIN": {
"description": "Origin Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "(VALUE) * 0x0001"
}
},
"VOLT_FINAL": {
"description": "The last Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": " (VALUE) * 0x0001"
}
},
"VOLT_STEP": {
"description": "Voltage Step",
"record_meta": true,
"initial": 0,
"domain": [
"STEP_MAX"
],
"value": {
"expression": "(VALUE + 1) * 0x0001"
}
},
"STEP_TIME": {
"description": "How much time between two step",
"record_meta": true,
"initial": 1,
"value": [
0.5,
1.0,
2.0
]
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"initial": 0,
"domain": [
200
],
"value": {
"expression": "VALUE + 1"
}
},
"DAC_VOLT": {
"description": "DAC output Voltage",
"record_meta": true,
"initial": 25000,
"domain": [
65536
]
},
"ADC_CHANNEL": {
"description": "read ADC data",
"record_meta": true,
"value": [
"ANA0",
"ANA1",
"ANA2",
"ANA3"
]
},
"ADC_LEVEL": {
"description": "ADC level",
"record_meta": true,
"initial": 3,
"value": [
"200k",
"10k",
"200R",
"auto"
],
"on_change": "set_adc_gain"
},
"Resister_LEVEL": {
"description": "Resister_LEVEL",
"record_meta": true,
"initial": 3,
"value": [
"Small",
"Middle",
"Large",
"Auto"
],
"on_change": "set_Resister_level"
},
"ADC_VALUE_V": {
"description": "ADC value voltage value",
"domain": "int"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"Const_Current_unit": {
"description": "Constant Current unit Setting",
"initial": 1,
"value": [
"uA",
"mA"
]
},
"Const_Current_value": {
"description": "Constant Current value Setting",
"initial": 0,
"domain": [
"Const_Current_Range"
],
"value": {
"expression": "(VALUE) * 0x00000001"
}
},
"Charge": {
"description": "Charge battery or not",
"initial": 0,
"value": [
"Charge",
"Discharge"
]
},
"Const_Current_VStop": {
"description": "Constant Current Stopping Voltage",
"initial": 0,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "(VALUE) * 0x0001"
}
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"2": "start_fun_gen",
"6": "adc_test",
"*": "start_data"
}
}
],
"start_fun_gen": [
"data_format",
"func_gen",
"VIS_STI"
],
"adc_test": [
"adc_test0",
"_cdr('1X;4X>ADC_VALUE_I;4X>ADC_VALUE_V')"
],
"start_data": [
"data_format",
"_notify(True)",
"set_sample_rate",
{
"expression": "MODE",
"when": {
"0": "curve_iv",
"1": "curve_cv",
"3": "curve_zt",
"4": "curve_vt",
"5": "curve_it",
"7": "const_current"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('I4V4Z4T4')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"curve_iv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN) * 0x0001",
"vb": "(VOLT_FINAL) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12"
},
"data": [
"1X10;2B>va;2B>vb;2B>dv;B>dt"
]
},
"curve_cv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN) * 0x0001",
"vb": "(VOLT_FINAL) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12",
"cn": "CYCLE_NUMBER+1"
},
"data": [
"1X20;2B>va;2B>vb;2B>dv;B>dt;B>cn"
]
},
"func_gen": {
"type": "RIS",
"parameter": {
"v": "(DAC_VOLT) * 0x0001"
},
"data": [
"X30;2B>v"
]
},
"curve_zt": {
"type": "RIS",
"data": [
"X40"
]
},
"curve_vt": {
"type": "RIS",
"data": [
"X50"
]
},
"curve_it": {
"type": "RIS",
"data": [
"X60"
]
},
"adc_test0": {
"type": "RIS",
"data": [
"X90;B>ADC_LEVEL;B>ADC_CHANNEL"
]
},
"set_sample_rate": {
"type": "RIS",
"data": [
"X70;B>SAMPLE_RATE"
]
},
"set_adc_gain": {
"type": "RIS",
"data": [
"X80;B>ADC_LEVEL"
]
},
"set_Resister_level": {
"type": "RIS",
"data": [
"XE0;B>Resister_LEVEL"
]
},
"const_current": {
"type": "RIS",
"data": [
"XD0;B>Charge;2X>Const_Current_VStop;4X>Const_Current_value"
]
}
}
}
@@ -1,5 +1,5 @@
{ {
"name": "Elite_EDC_1.4", "name": "EliteZM02",
"version": "1.2.30", "version": "1.2.30",
"match_rule": { "match_rule": {
"local_name_pattern": "Elite.*", "local_name_pattern": "Elite.*",
@@ -492,6 +492,14 @@
"1XC0;1X02;4B>ve;2B>vf;4B>vg;B>cn" "1XC0;1X02;4B>ve;2B>vf;4B>vg;B>cn"
] ]
}, },
"VIS_CC_ZERO": [
"_data_format('I4V4Z4T4')",
"_disable_cache(False)",
"_notify(True)",
"VIS_CC_ZERO",
"_sync(True)",
"VIS_STI"
],
"curve_cv3_high_cycle": { "curve_cv3_high_cycle": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
@@ -1,15 +1,14 @@
{ {
"name": "Elite_BAT_0.1", "name": "EliteZM15",
"version": "1.2.30", "version": "1.2.30",
"match_rule": { "match_rule": {
"local_name_pattern": "Elite.*", "local_name_pattern": "Elite.*",
"major_product_number": 0, "major_product_number": 0,
"minor_product_number": 3, "minor_product_number": 2,
"major_version_number": 1, "major_version_number": 1,
"minor_version_number": 0 "minor_version_number": 6
}, },
"constant": { "constant": {
"TIME_MAX": 100000,
"VOLT_MAX": 65536, "VOLT_MAX": 65536,
"Const_Current_Range": 1500001, "Const_Current_Range": 1500001,
"BLE_WRITE_MAX": 255 "BLE_WRITE_MAX": 255
@@ -18,7 +17,7 @@
"DPV_e_init": { "DPV_e_init": {
"description": "DPV initial voltage ", "description": "DPV initial voltage ",
"record_meta": true, "record_meta": true,
"initial": 20000, "initial":20000,
"domain": [ "domain": [
50001 50001
], ],
@@ -29,7 +28,7 @@
"DPV_e_final": { "DPV_e_final": {
"description": "DPV final voltage", "description": "DPV final voltage",
"record_meta": true, "record_meta": true,
"initial": 30000, "initial":30000,
"domain": [ "domain": [
50001 50001
], ],
@@ -40,7 +39,7 @@
"DPV_e_1": { "DPV_e_1": {
"description": "DPV voltage return 1", "description": "DPV voltage return 1",
"record_meta": true, "record_meta": true,
"initial": 35000, "initial":35000,
"domain": [ "domain": [
50001 50001
], ],
@@ -51,7 +50,7 @@
"DPV_e_2": { "DPV_e_2": {
"description": "DPV voltage return 2", "description": "DPV voltage return 2",
"record_meta": true, "record_meta": true,
"initial": 15000, "initial":15000,
"domain": [ "domain": [
50001 50001
], ],
@@ -62,7 +61,7 @@
"DPV_amp": { "DPV_amp": {
"description": "DPV pulse amplitude", "description": "DPV pulse amplitude",
"record_meta": true, "record_meta": true,
"initial": 25125, "initial":25125,
"domain": [ "domain": [
50001 50001
], ],
@@ -73,7 +72,7 @@
"DPV_pul_width": { "DPV_pul_width": {
"description": "DPV pulse width", "description": "DPV pulse width",
"record_meta": true, "record_meta": true,
"initial": 50, "initial":50,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -84,7 +83,7 @@
"DPV_increment": { "DPV_increment": {
"description": "DPV increment", "description": "DPV increment",
"record_meta": true, "record_meta": true,
"initial": 25025, "initial":25025,
"domain": [ "domain": [
50001 50001
], ],
@@ -95,7 +94,7 @@
"DPV_step_time": { "DPV_step_time": {
"description": "DPV step time = time of one period", "description": "DPV step time = time of one period",
"record_meta": true, "record_meta": true,
"initial": 500, "initial":500,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -106,7 +105,7 @@
"DPV_notify_rate": { "DPV_notify_rate": {
"description": "DPV sample rate", "description": "DPV sample rate",
"record_meta": true, "record_meta": true,
"initial": 1000, "initial":1000,
"domain": [ "domain": [
1001 1001
], ],
@@ -114,24 +113,10 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"DPV_curr_rec": {
"description": "DPV current recording period start",
"record_meta": true,
"initial": [
25,
100
],
"domain": {
"list": [
101
]
},
"value": "VALUE"
},
"DPV_curr_rec_min": { "DPV_curr_rec_min": {
"description": "DPV current recording period start", "description": "DPV current recording period start",
"record_meta": true, "record_meta": true,
"initial": 25, "initial":25,
"domain": [ "domain": [
101 101
], ],
@@ -142,7 +127,7 @@
"DPV_curr_rec_max": { "DPV_curr_rec_max": {
"description": "DPV current recording period end", "description": "DPV current recording period end",
"record_meta": true, "record_meta": true,
"initial": 100, "initial":100,
"domain": [ "domain": [
101 101
], ],
@@ -153,8 +138,11 @@
"DPV_pulse_option": { "DPV_pulse_option": {
"description": "Invert of pulse option", "description": "Invert of pulse option",
"record_meta": true, "record_meta": true,
"domain": "bool", "initial": 0,
"initial": false "value": [
0,
1
]
}, },
"DPV_mode": { "DPV_mode": {
"description": "AUTO: 0 / ADVANCED: 1/ ENGINEERING: 2", "description": "AUTO: 0 / ADVANCED: 1/ ENGINEERING: 2",
@@ -169,29 +157,16 @@
"DPV_engineering_enable": { "DPV_engineering_enable": {
"description": "Engineering mode switch", "description": "Engineering mode switch",
"record_meta": true, "record_meta": true,
"domain": "bool", "initial": 0,
"initial": false "value": [
}, 0,
"V_initial": { 1
"description": "Pulse Mode Segment Voltage", ]
"record_meta": true,
"initial": [
50000,
25000,
25000,
25000
],
"domain": {
"list": [
50001
]
},
"value": "VALUE"
}, },
"V_initial_0": { "V_initial_0": {
"description": "Pulse Mode Segment Voltage 0", "description": "Pulse Mode Segment Voltage 0",
"record_meta": true, "record_meta": true,
"initial": 50000, "initial":50000,
"domain": [ "domain": [
50001 50001
], ],
@@ -202,7 +177,7 @@
"V_initial_1": { "V_initial_1": {
"description": "Pulse Mode Segment Voltage 1", "description": "Pulse Mode Segment Voltage 1",
"record_meta": true, "record_meta": true,
"initial": 25000, "initial":25000,
"domain": [ "domain": [
50001 50001
], ],
@@ -213,7 +188,7 @@
"V_initial_2": { "V_initial_2": {
"description": "Pulse Mode Segment Voltage 2", "description": "Pulse Mode Segment Voltage 2",
"record_meta": true, "record_meta": true,
"initial": 25000, "initial":25000,
"domain": [ "domain": [
50001 50001
], ],
@@ -224,7 +199,7 @@
"V_initial_3": { "V_initial_3": {
"description": "Pulse Mode Segment Voltage 3", "description": "Pulse Mode Segment Voltage 3",
"record_meta": true, "record_meta": true,
"initial": 25000, "initial":25000,
"domain": [ "domain": [
50001 50001
], ],
@@ -232,26 +207,10 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"t_pulse": {
"description": "Pulse Mode Segment Duration",
"record_meta": true,
"initial": [
750,
9250,
1000,
1000
],
"domain": {
"list": [
1000001
]
},
"value": "VALUE"
},
"t_pulse_0": { "t_pulse_0": {
"description": "Pulse Mode Segment Duration 0", "description": "Pulse Mode Segment Duration 0",
"record_meta": true, "record_meta": true,
"initial": 750, "initial":750,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -262,7 +221,7 @@
"t_pulse_1": { "t_pulse_1": {
"description": "Pulse Mode Segment Duration 1", "description": "Pulse Mode Segment Duration 1",
"record_meta": true, "record_meta": true,
"initial": 9250, "initial":9250,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -273,7 +232,7 @@
"t_pulse_2": { "t_pulse_2": {
"description": "Pulse Mode Segment Duration 2", "description": "Pulse Mode Segment Duration 2",
"record_meta": true, "record_meta": true,
"initial": 1000, "initial":1000,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -284,7 +243,7 @@
"t_pulse_3": { "t_pulse_3": {
"description": "Pulse Mode Segment Duration 3", "description": "Pulse Mode Segment Duration 3",
"record_meta": true, "record_meta": true,
"initial": 1000, "initial":1000,
"domain": [ "domain": [
1000001 1000001
], ],
@@ -292,40 +251,10 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"CURR_REC": {
"description": "Pulse Mode Current Recording Period",
"record_meta": true,
"initial": [
[
35,
90
],
[
35,
99
],
[
35,
95
],
[
35,
95
]
],
"domain": {
"list": {
"list": [
100
]
}
},
"value": "VALUE"
},
"CURR_REC_START_0": { "CURR_REC_START_0": {
"description": "Pulse Mode Current Recording Period Start", "description": "Pulse Mode Current Recording Period Start",
"record_meta": true, "record_meta": true,
"initial": 35, "initial":35,
"domain": [ "domain": [
100 100
], ],
@@ -336,7 +265,7 @@
"CURR_REC_START_1": { "CURR_REC_START_1": {
"description": "Pulse Mode Current Recording Period Start", "description": "Pulse Mode Current Recording Period Start",
"record_meta": true, "record_meta": true,
"initial": 35, "initial":35,
"domain": [ "domain": [
100 100
], ],
@@ -347,7 +276,7 @@
"CURR_REC_START_2": { "CURR_REC_START_2": {
"description": "Pulse Mode Current Recording Period Start", "description": "Pulse Mode Current Recording Period Start",
"record_meta": true, "record_meta": true,
"initial": 35, "initial":35,
"domain": [ "domain": [
100 100
], ],
@@ -358,7 +287,7 @@
"CURR_REC_START_3": { "CURR_REC_START_3": {
"description": "Pulse Mode Current Recording Period Start", "description": "Pulse Mode Current Recording Period Start",
"record_meta": true, "record_meta": true,
"initial": 35, "initial":35,
"domain": [ "domain": [
100 100
], ],
@@ -369,7 +298,7 @@
"CURR_REC_END_0": { "CURR_REC_END_0": {
"description": "Pulse Mode Current Recording Period End", "description": "Pulse Mode Current Recording Period End",
"record_meta": true, "record_meta": true,
"initial": 90, "initial":90,
"domain": [ "domain": [
100 100
], ],
@@ -380,7 +309,7 @@
"CURR_REC_END_1": { "CURR_REC_END_1": {
"description": "Pulse Mode Current Recording Period End", "description": "Pulse Mode Current Recording Period End",
"record_meta": true, "record_meta": true,
"initial": 99, "initial":99,
"domain": [ "domain": [
100 100
], ],
@@ -391,7 +320,7 @@
"CURR_REC_END_2": { "CURR_REC_END_2": {
"description": "Pulse Mode Current Recording Period End", "description": "Pulse Mode Current Recording Period End",
"record_meta": true, "record_meta": true,
"initial": 95, "initial":95,
"domain": [ "domain": [
100 100
], ],
@@ -402,7 +331,7 @@
"CURR_REC_END_3": { "CURR_REC_END_3": {
"description": "Pulse Mode Current Recording Period End", "description": "Pulse Mode Current Recording Period End",
"record_meta": true, "record_meta": true,
"initial": 95, "initial":95,
"domain": [ "domain": [
100 100
], ],
@@ -410,40 +339,6 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"segment_ui_order": {
"description": "Pulse Mode Segment Order",
"record_meta": true,
"initial": [
0,
1,
-1,
-1
],
"domain": {
"list": [
-1,
6
]
},
"value": "VALUE"
},
"segment_order": {
"description": "Pulse Mode Segment Order",
"record_meta": true,
"initial": [
0,
1,
-1,
-1
],
"domain": {
"list": [
-1,
6
]
},
"value": "VALUE"
},
"segment_ui_order_0": { "segment_ui_order_0": {
"description": "Pulse Mode Segment UI Order 0, value: segment ID + 1", "description": "Pulse Mode Segment UI Order 0, value: segment ID + 1",
"record_meta": true, "record_meta": true,
@@ -583,14 +478,13 @@
"Constant Current", "Constant Current",
"Cyclic Voltammetry", "Cyclic Voltammetry",
"Linear Sweep Voltammetry", "Linear Sweep Voltammetry",
"Chronoamperometric", "Chronoamperometric Graph",
"Cali DAC - test", "Cali DAC - test",
"Cali ADC - test", "Cali ADC - test",
"Dev Mode", "Dev Mode",
"Open Circuit Potential", "Open Circuit Potential",
"Pulse Sensing", "Pulse Sensing",
"Differential Pulse Voltammetry (DPV)", "Differential Pulse Voltammetry (DPV)"
"Chronopotentiometry"
] ]
}, },
"VOLT_ORIGIN": { "VOLT_ORIGIN": {
@@ -750,7 +644,7 @@
}, },
"CURRNET_LIMIT_VALUE": { "CURRNET_LIMIT_VALUE": {
"description": "Current value Setting", "description": "Current value Setting",
"initial": 1500000, "initial":1500000,
"domain": [ "domain": [
"Const_Current_Range" "Const_Current_Range"
], ],
@@ -780,7 +674,7 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"CA_VOLT": { "VOLT_VSCAN": {
"description": "Voltage of VScan", "description": "Voltage of VScan",
"record_meta": true, "record_meta": true,
"initial": 25000, "initial": 25000,
@@ -791,15 +685,6 @@
"expression": "VALUE" "expression": "VALUE"
} }
}, },
"CC_CP_SPEED": {
"description": "charge speed for CC and CP mode",
"initial": 1,
"value": [
"low",
"normal",
"high"
]
},
"ADC_LEVEL_I_15": { "ADC_LEVEL_I_15": {
"description": "ADC level", "description": "ADC level",
"record_meta": true, "record_meta": true,
@@ -877,17 +762,6 @@
"s", "s",
"ms" "ms"
] ]
},
"TIME_DURATION": {
"description": "Run duration",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
} }
}, },
"instruction": { "instruction": {
@@ -938,8 +812,7 @@
"7": "set_adc_gain_I", "7": "set_adc_gain_I",
"8": "set_adc_gain_I", "8": "set_adc_gain_I",
"9": "set_adc_gain_I", "9": "set_adc_gain_I",
"13": "set_adc_gain_I", "13": "set_adc_gain_I"
"16": "set_adc_gain_I"
} }
}, },
{ {
@@ -954,8 +827,7 @@
"7": "set_adc_gain_Vin", "7": "set_adc_gain_Vin",
"8": "set_adc_gain_Vin", "8": "set_adc_gain_Vin",
"9": "set_adc_gain_Vin", "9": "set_adc_gain_Vin",
"13": "set_adc_gain_Vin", "13": "set_adc_gain_Vin"
"16": "set_adc_gain_Vin"
} }
}, },
{ {
@@ -973,8 +845,7 @@
"9": "curve_const_vscan", "9": "curve_const_vscan",
"13": "curve_ocp", "13": "curve_ocp",
"14": "curve_pulse_sensing", "14": "curve_pulse_sensing",
"15": "curve_dpv", "15": "curve_dpv"
"16": "curve_cp"
} }
}, },
{ {
@@ -1172,7 +1043,6 @@
"vb": "Const_Current_value", "vb": "Const_Current_value",
"vc": "VOLTSTOP_MAX", "vc": "VOLTSTOP_MAX",
"vd": "VOLTSTOP_MIN", "vd": "VOLTSTOP_MIN",
"ve": "CC_CP_SPEED",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1184,31 +1054,7 @@
"B>va;4B>vb;2B>vc;2B>vd;", "B>va;4B>vb;2B>vc;2B>vd;",
"4b>pa;4b>pb;", "4b>pa;4b>pb;",
"4b>pc;4b>pd;", "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": [
"X0C;",
"B>va;4B>vb;2B>vc;2B>vd;",
"4b>pa;4b>pb;",
"4b>pc;4b>pd;",
"2B>pe;",
"4b>0b0000;4b>ve"
] ]
}, },
"curve_cv3": { "curve_cv3": {
@@ -1301,7 +1147,7 @@
"curve_const_vscan": { "curve_const_vscan": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
"va": "CA_VOLT", "va": "VOLT_VSCAN",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1334,67 +1180,58 @@
}, },
"curve_pulse_sensing": [ "curve_pulse_sensing": [
{ {
"expression": "segment_ui_order[0]", "expression": "segment_ui_order_0",
"when": { "when": {
"0": "pul_sen_segment_0", "1": "pul_sen_segment_0",
"1": "pul_sen_segment_1", "2": "pul_sen_segment_1",
"2": "pul_sen_segment_2", "3": "pul_sen_segment_2",
"3": "pul_sen_segment_3" "4": "pul_sen_segment_3"
} }
}, },
{ {
"expression": "segment_ui_order[1]", "expression": "segment_ui_order_1",
"when": { "when": {
"0": "pul_sen_segment_0", "1": "pul_sen_segment_0",
"1": "pul_sen_segment_1", "2": "pul_sen_segment_1",
"2": "pul_sen_segment_2", "3": "pul_sen_segment_2",
"3": "pul_sen_segment_3" "4": "pul_sen_segment_3"
} }
}, },
{ {
"expression": "segment_ui_order[2]", "expression": "segment_ui_order_2",
"when": { "when": {
"0": "pul_sen_segment_0", "1": "pul_sen_segment_0",
"1": "pul_sen_segment_1", "2": "pul_sen_segment_1",
"2": "pul_sen_segment_2", "3": "pul_sen_segment_2",
"3": "pul_sen_segment_3" "4": "pul_sen_segment_3"
} }
}, },
{ {
"expression": "segment_ui_order[3]", "expression": "segment_ui_order_3",
"when": { "when": {
"0": "pul_sen_segment_0", "1": "pul_sen_segment_0",
"1": "pul_sen_segment_1", "2": "pul_sen_segment_1",
"2": "pul_sen_segment_2", "3": "pul_sen_segment_2",
"3": "pul_sen_segment_3" "4": "pul_sen_segment_3"
} }
}, },
"pul_sen_start" "pul_sen_start"
], ],
"pul_sen_start": { "pul_sen_start": {
"type": "RIS", "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": [ "data": [
"1X0D;", "1X0D;",
"1XFF", "1XFF"
"4b>pa;4b>pb;",
"4b>pc;4b>pd"
] ]
}, },
"pul_sen_segment_0": { "pul_sen_segment_0": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
"va": "V_initial[0]", "va": "V_initial_0",
"vb": "t_pulse[0]", "vb": "t_pulse_0",
"vc": "CURR_REC[0][0]", "vc": "CURR_REC_START_0",
"vd": "CURR_REC[0][1]", "vd": "CURR_REC_END_0",
"ve": "segment_order.index(segment_order[0])", "ve": "segment_order_0 - 1",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1410,11 +1247,11 @@
"pul_sen_segment_1": { "pul_sen_segment_1": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
"va": "V_initial[1]", "va": "V_initial_1",
"vb": "t_pulse[1]", "vb": "t_pulse_1",
"vc": "CURR_REC[1][0]", "vc": "CURR_REC_START_1",
"vd": "CURR_REC[1][1]", "vd": "CURR_REC_END_1",
"ve": "segment_order.index(segment_order[1])", "ve": "segment_order_1 - 1",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1430,11 +1267,11 @@
"pul_sen_segment_2": { "pul_sen_segment_2": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
"va": "V_initial[2]", "va": "V_initial_2",
"vb": "t_pulse[2]", "vb": "t_pulse_2",
"vc": "CURR_REC[2][0]", "vc": "CURR_REC_START_2",
"vd": "CURR_REC[2][1]", "vd": "CURR_REC_END_2",
"ve": "segment_order.index(segment_order[2])", "ve": "segment_order_2 - 1",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1450,11 +1287,11 @@
"pul_sen_segment_3": { "pul_sen_segment_3": {
"type": "RIS", "type": "RIS",
"parameter": { "parameter": {
"va": "V_initial[3]", "va": "V_initial_3",
"vb": "t_pulse[3]", "vb": "t_pulse_3",
"vc": "CURR_REC[3][0]", "vc": "CURR_REC_START_3",
"vd": "CURR_REC[3][1]", "vd": "CURR_REC_END_3",
"ve": "segment_order.index(segment_order[3])", "ve": "segment_order_3 - 1",
"pa": "ADC_LEVEL_I_15", "pa": "ADC_LEVEL_I_15",
"pb": "ADC_LEVEL_V_IN_15", "pb": "ADC_LEVEL_V_IN_15",
"pc": "DAC_LEVEL_V_OUT_15", "pc": "DAC_LEVEL_V_OUT_15",
@@ -1467,7 +1304,9 @@
"2B>va;4B>vb;1B>vc;1B>vd;1B>ve" "2B>va;4B>vb;1B>vc;1B>vd;1B>ve"
] ]
}, },
"curve_dpv": [
"curve_dpv":[
{ {
"expression": "DPV_mode", "expression": "DPV_mode",
"when": { "when": {
@@ -1515,8 +1354,8 @@
"parameter": { "parameter": {
"va": "DPV_step_time", "va": "DPV_step_time",
"vb": "DPV_notify_rate", "vb": "DPV_notify_rate",
"vc": "DPV_curr_rec[0]", "vc": "DPV_curr_rec_min",
"vd": "DPV_curr_rec[1]" "vd": "DPV_curr_rec_max"
}, },
"data": [ "data": [
"1X0E;", "1X0E;",
@@ -1526,20 +1365,14 @@
}, },
"dpv_auto_mode_ins_final": { "dpv_auto_mode_ins_final": {
"type": "RIS", "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": [ "data": [
"1X0E;", "1X0E;",
"1XFF;", "1XFF"
"4b>pa;4b>pb;",
"4b>pc;4b>pd"
] ]
}, },
"dpv_advanced_mode": [ "dpv_advanced_mode": [
"dpv_advanced_mode_ins_1", "dpv_advanced_mode_ins_1",
"dpv_advanced_mode_ins_2", "dpv_advanced_mode_ins_2",
@@ -1579,8 +1412,8 @@
"parameter": { "parameter": {
"va": "DPV_step_time", "va": "DPV_step_time",
"vb": "DPV_notify_rate", "vb": "DPV_notify_rate",
"vc": "DPV_curr_rec[0]", "vc": "DPV_curr_rec_min",
"vd": "DPV_curr_rec[1]" "vd": "DPV_curr_rec_max"
}, },
"data": [ "data": [
"1X10;", "1X10;",
@@ -1604,18 +1437,9 @@
}, },
"dpv_advanced_mode_ins_final": { "dpv_advanced_mode_ins_final": {
"type": "RIS", "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": [ "data": [
"1X10;", "1X10;",
"1XFF;", "1XFF"
"4b>pa;4b>pb;",
"4b>pc;4b>pd"
] ]
}, },
"dpv_engineering_mode": [ "dpv_engineering_mode": [
@@ -1633,6 +1457,18 @@
"dpv_engineering_mode_advanced": [ "dpv_engineering_mode_advanced": [
"dpv_advanced_mode" "dpv_advanced_mode"
], ],
"VIS_CC_ZERO": [
"_data_format('I4V4Z4T4')",
"_disable_cache(False)",
"_notify(True)",
"set_adc_gain_I",
"set_adc_gain_Vin",
"VIS_CC_ZERO",
"_sync(True)",
"VIS_STI"
],
"ble_instru_send": [ "ble_instru_send": [
"ble_write", "ble_write",
"_cdr('20X>ADC_VALUE_I')" "_cdr('20X>ADC_VALUE_I')"
@@ -1663,4 +1499,6 @@
] ]
} }
} }
} }
@@ -0,0 +1,320 @@
{
"name": "EliteZML",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-ZM.+",
"major_product_number": 0,
"minor_product_number": 2,
"major_version_number": 1,
"minor_version_number": 4
},
"constant": {
"ADC_CHANNEL_NUMBER": [
12,
13,
14,
15
],
"VOLT_MAX": 65536,
"STEP_MAX": 100,
"Const_Current_Range": 3000001
},
"parameters": {
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "channel label",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"value": [
100,
10,
5,
1,
0.1,
0.01
],
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"I-V Curve",
"Cyclic Voltammetry",
"Function Generator",
"R-T Curve",
"V-T Curve",
"I-T Curve",
"ADC test",
"Constant Current"
]
},
"VOLT_ORIGIN": {
"description": "Origin Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "(VALUE + 1) * 0x0001"
}
},
"VOLT_FINAL": {
"description": "The last Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": " (VALUE + 1) * 0x0001"
}
},
"VOLT_STEP": {
"description": "Voltage Step",
"record_meta": true,
"initial": 0,
"domain": [
"STEP_MAX"
],
"value": {
"expression": "(VALUE + 1) * 0x0001"
}
},
"STEP_TIME": {
"description": "How much time between two step",
"record_meta": true,
"initial": 1,
"value": [
0.5,
1.0,
2.0
]
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"domain": [
200
],
"value": {
"expression": "VALUE + 1"
}
},
"DAC_VOLT": {
"description": "DAC output Voltage",
"record_meta": true,
"domain": [
65536
]
},
"ADC_CHANNEL": {
"description": "read ADC data",
"record_meta": true,
"value": [
"ANA0",
"ANA1",
"ANA2",
"ANA3"
]
},
"ADC_LEVEL": {
"description": "ADC level",
"record_meta": true,
"value": [
"200k",
"10k",
"200R",
"auto"
],
"on_change": "set_adc_gain"
},
"Resister_LEVEL": {
"description": "Resister_LEVEL",
"record_meta": true,
"value": [
"Small",
"Middle",
"Large",
"Auto"
],
"on_change": "set_Resister_level"
},
"ADC_VALUE_V": {
"description": "ADC value voltage value",
"domain": "int"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"Const_Current_unit": {
"description": "Constant Current unit Setting",
"value": [
"uA",
"mA"
]
},
"Const_Current_value": {
"description": "Constant Current value Setting",
"initial": 1500000,
"domain": [
"Const_Current_Range"
],
"value": {
"expression": "(VALUE) * 0x00000001"
}
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"2": "start_fun_gen",
"6": "adc_test",
"*": "start_data"
}
}
],
"start_fun_gen": [
"data_format",
"func_gen",
"VIS_STI"
],
"adc_test": [
"adc_test0",
"_cdr('1X;4X>ADC_VALUE_I;4X>ADC_VALUE_V')"
],
"start_data": [
"data_format",
"_notify(True)",
"set_sample_rate",
{
"expression": "MODE",
"when": {
"0": "curve_iv",
"1": "curve_cv",
"3": "curve_zt",
"4": "curve_vt",
"5": "curve_it",
"7": "const_current"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('I4V4Z4T4')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"curve_iv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN + 1) * 0x0001",
"vb": "(VOLT_FINAL + 1) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12"
},
"data": [
"1X10;2B>va;2B>vb;2B>dv;B>dt"
]
},
"curve_cv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN + 1) * 0x0001",
"vb": "(VOLT_FINAL + 1) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12",
"cn": "CYCLE_NUMBER+1"
},
"data": [
"1X20;2B>va;2B>vb;2B>dv;B>dt;B>CYCLE_NUMBER"
]
},
"func_gen": {
"type": "RIS",
"parameter": {
"v": "(DAC_VOLT) * 0x0001"
},
"data": [
"X30;2B>v"
]
},
"curve_zt": {
"type": "RIS",
"data": [
"X40"
]
},
"curve_vt": {
"type": "RIS",
"data": [
"X50"
]
},
"curve_it": {
"type": "RIS",
"data": [
"X60"
]
},
"adc_test0": {
"type": "RIS",
"data": [
"X90;B>ADC_LEVEL;B>ADC_CHANNEL"
]
},
"set_sample_rate": {
"type": "RIS",
"data": [
"X70;B>SAMPLE_RATE"
]
},
"set_adc_gain": {
"type": "RIS",
"data": [
"X80;B>ADC_LEVEL"
]
},
"set_Resister_level": {
"type": "RIS",
"data": [
"XE0;B>Resister_LEVEL"
]
},
"const_current": {
"type": "RIS",
"data": [
"XD0;4X>Const_Current_value"
]
}
}
}
@@ -0,0 +1,320 @@
{
"name": "EliteZMS",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-ZM.+",
"major_product_number": 0,
"minor_product_number": 2,
"major_version_number": 1,
"minor_version_number": 3
},
"constant": {
"ADC_CHANNEL_NUMBER": [
12,
13,
14,
15
],
"VOLT_MAX": 65536,
"STEP_MAX": 100,
"Const_Current_Range": 3000001
},
"parameters": {
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "channel label",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"value": [
100,
10,
5,
1,
0.1,
0.01
],
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"value": [
"I-V Curve",
"Cyclic Voltammetry",
"Function Generator",
"R-T Curve",
"V-T Curve",
"I-T Curve",
"ADC test",
"Constant Current"
]
},
"VOLT_ORIGIN": {
"description": "Origin Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": "(VALUE + 1) * 0x0001"
}
},
"VOLT_FINAL": {
"description": "The last Voltage of Scan",
"record_meta": true,
"initial": 25000,
"domain": [
"VOLT_MAX"
],
"value": {
"expression": " (VALUE + 1) * 0x0001"
}
},
"VOLT_STEP": {
"description": "Voltage Step",
"record_meta": true,
"initial": 0,
"domain": [
"STEP_MAX"
],
"value": {
"expression": "(VALUE + 1) * 0x0001"
}
},
"STEP_TIME": {
"description": "How much time between two step",
"record_meta": true,
"initial": 1,
"value": [
0.5,
1.0,
2.0
]
},
"CYCLE_NUMBER": {
"description": "CV cycle number",
"record_meta": true,
"domain": [
200
],
"value": {
"expression": "VALUE + 1"
}
},
"DAC_VOLT": {
"description": "DAC output Voltage",
"record_meta": true,
"domain": [
65536
]
},
"ADC_CHANNEL": {
"description": "read ADC data",
"record_meta": true,
"value": [
"ANA0",
"ANA1",
"ANA2",
"ANA3"
]
},
"ADC_LEVEL": {
"description": "ADC level",
"record_meta": true,
"value": [
"200k",
"10k",
"200R",
"auto"
],
"on_change": "set_adc_gain"
},
"Resister_LEVEL": {
"description": "Resister_LEVEL",
"record_meta": true,
"value": [
"Small",
"Middle",
"Large",
"Auto"
],
"on_change": "set_Resister_level"
},
"ADC_VALUE_V": {
"description": "ADC value voltage value",
"domain": "int"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"Const_Current_unit": {
"description": "Constant Current unit Setting",
"value": [
"uA",
"mA"
]
},
"Const_Current_value": {
"description": "Constant Current value Setting",
"initial": 1500000,
"domain": [
"Const_Current_Range"
],
"value": {
"expression": "(VALUE) * 0x00000001"
}
}
},
"instruction": {
"start": [
{
"expression": "MODE",
"when": {
"2": "start_fun_gen",
"6": "adc_test",
"*": "start_data"
}
}
],
"start_fun_gen": [
"data_format",
"func_gen",
"VIS_STI"
],
"adc_test": [
"adc_test0",
"_cdr('1X;4X>ADC_VALUE_I;4X>ADC_VALUE_V')"
],
"start_data": [
"data_format",
"_notify(True)",
"set_sample_rate",
{
"expression": "MODE",
"when": {
"0": "curve_iv",
"1": "curve_cv",
"3": "curve_zt",
"4": "curve_vt",
"5": "curve_it",
"7": "const_current"
}
},
"_sync(True)",
"VIS_STI"
],
"data_format": [
"_data_format('I4V4Z4T4')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"curve_iv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN + 1) * 0x0001",
"vb": "(VOLT_FINAL + 1) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12"
},
"data": [
"1X10;2B>va;2B>vb;2B>dv;B>dt"
]
},
"curve_cv": {
"type": "RIS",
"parameter": {
"va": "(VOLT_ORIGIN + 1) * 0x0001",
"vb": "(VOLT_FINAL + 1) * 0x0001",
"dv": "(VOLT_STEP + 1) * 0x0001",
"dt": "STEP_TIME * 0x12",
"cn": "CYCLE_NUMBER+1"
},
"data": [
"1X20;2B>va;2B>vb;2B>dv;B>dt;B>CYCLE_NUMBER"
]
},
"func_gen": {
"type": "RIS",
"parameter": {
"v": "(DAC_VOLT) * 0x0001"
},
"data": [
"X30;2B>v"
]
},
"curve_zt": {
"type": "RIS",
"data": [
"X40"
]
},
"curve_vt": {
"type": "RIS",
"data": [
"X50"
]
},
"curve_it": {
"type": "RIS",
"data": [
"X60"
]
},
"adc_test0": {
"type": "RIS",
"data": [
"X90;B>ADC_LEVEL;B>ADC_CHANNEL"
]
},
"set_sample_rate": {
"type": "RIS",
"data": [
"X70;B>SAMPLE_RATE"
]
},
"set_adc_gain": {
"type": "RIS",
"data": [
"X80;B>ADC_LEVEL"
]
},
"set_Resister_level": {
"type": "RIS",
"data": [
"XE0;B>Resister_LEVEL"
]
},
"const_current": {
"type": "RIS",
"data": [
"XD0;4X>Const_Current_value"
]
}
}
}
@@ -1,12 +1,12 @@
{ {
"name": "Elite_MEGAFLY_0.1", "name": "EliteZM_pulsefly",
"version": "1.2.30", "version": "1.2.30",
"match_rule": { "match_rule": {
"local_name_pattern": "Elite.*", "local_name_pattern": "Elite.*",
"major_product_number": 0, "major_product_number": 0,
"minor_product_number": 6, "minor_product_number": 2,
"major_version_number": 1, "major_version_number": 1,
"minor_version_number": 0 "minor_version_number": 7
}, },
"constant": { "constant": {
"ADC_CHANNEL_NUMBER": [ "ADC_CHANNEL_NUMBER": [
@@ -1426,6 +1426,14 @@
"X80;X03;B>CTRL_HIGH_Z_15" "X80;X03;B>CTRL_HIGH_Z_15"
] ]
}, },
"VIS_CC_ZERO": [
"_data_format('I4V4Z4T4')",
"_disable_cache(False)",
"_notify(True)",
"VIS_CC_ZERO",
"_sync(True)",
"VIS_STI"
],
"pulse_fly": [ "pulse_fly": [
"sti_mode", "sti_mode",
"gas1_mode", "gas1_mode",
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
find /home/pi -maxdepth 1 -type f -iname '*.out' -mtime +30 -delete
-38
View File
@@ -1,38 +0,0 @@
#!/bin/bash
# install python module numpy
sudo apt-get install libatlas-base-dev
sudo apt-get install python3-numpy
# install python module SQLAlchemy
pip3 install SQLAlchemy
# create folder to save project logger
mkdir -p /home/pi/logger/project
# add column cycle in projects
sudo su -c "psql -d postgres -c \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS cycle JSONB;\"" postgres
# add column project in recording_data_metas
sudo su -c "psql -d postgres -c \"ALTER TABLE recording_data_metas ADD COLUMN IF NOT EXISTS project Int4;\"" postgres
# remove default controller info
sudo su -c "psql -d postgres -c \"DELETE FROM controllers WHERE name='WTP_NONE';\"" postgres
# change table name from meta_project_info to project_metas
sudo su -c "psql -d postgres -c \"ALTER TABLE IF EXISTS meta_project_info RENAME TO project_metas;\"" postgres
# change table name from project_report to project_reports
sudo su -c "psql -d postgres -c \"ALTER TABLE IF EXISTS project_report RENAME TO project_reports;\"" postgres
# change table project_meta column cycle to type jsonb
sudo su -c "psql -d postgres -c \"ALTER TABLE project_metas ALTER COLUMN cycle type jsonb USING (cycle::jsonb);\"" postgres
# drop column calibration default
sudo su -c "psql -d postgres -c \"ALTER TABLE devices ALTER COLUMN calibration DROP DEFAULT;\"" postgres
# drop column calibration default
sudo su -c "psql -d postgres -c \"ALTER TABLE devices ALTER COLUMN calibration TYPE bytea USING calibration::bytea;\"" postgres
# add column project in recording_data_metas
sudo su -c "psql -d postgres -c \"ALTER TABLE devices ADD COLUMN IF NOT EXISTS calibration_version Int4 DEFAULT -1;\"" postgres