Compare commits
14 Commits
51913f2e2f
...
1896e2534b
Author | SHA1 | Date |
---|---|---|
linuswck | 1896e2534b | |
linuswck | 6d4b1b0574 | |
linuswck | 0b5fda2cd9 | |
linuswck | b763350a8b | |
linuswck | 838592c812 | |
linuswck | e632cbbfdd | |
linuswck | 27bf573010 | |
linuswck | c267c30b89 | |
linuswck | c5826876a6 | |
linuswck | 6782cda790 | |
linuswck | 2fe2ef531b | |
linuswck | f2ad06ecae | |
linuswck | 5166bb7ba8 | |
linuswck | 9c611fc861 |
|
@ -73,7 +73,7 @@
|
||||||
src = "${self}/pykirdy";
|
src = "${self}/pykirdy";
|
||||||
|
|
||||||
nativeBuildInputs = [ pkgs.qt6.wrapQtAppsHook ];
|
nativeBuildInputs = [ pkgs.qt6.wrapQtAppsHook ];
|
||||||
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync pglive aenum]);
|
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync pglive aenum sipyco]);
|
||||||
|
|
||||||
dontWrapQtApps = true;
|
dontWrapQtApps = true;
|
||||||
postFixup = ''
|
postFixup = ''
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
rust openocd dfu-util glibc
|
rust openocd dfu-util glibc
|
||||||
] ++ (with python3Packages; [
|
] ++ (with python3Packages; [
|
||||||
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive aenum
|
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive aenum sipyco
|
||||||
]);
|
]);
|
||||||
shellHook=
|
shellHook=
|
||||||
''
|
''
|
||||||
|
|
|
@ -2,147 +2,135 @@ from pprint import pp
|
||||||
from driver.kirdy import Kirdy, FilterConfig
|
from driver.kirdy import Kirdy, FilterConfig
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def enter_dfu_mode(kirdy: Kirdy):
|
||||||
"""
|
"""
|
||||||
Enter Device Firmware Upgrade(Dfu) mode
|
Enter Device Firmware Upgrade(Dfu) mode
|
||||||
Please see README.md for flash instructions.
|
Please refer to README.md for firmware update instructions.
|
||||||
"""
|
"""
|
||||||
def enter_dfu_mode(kirdy: Kirdy):
|
await kirdy.device.dfu()
|
||||||
kirdy.device.dfu()
|
|
||||||
kirdy.end_session()
|
|
||||||
|
|
||||||
|
async def active_report(kirdy: Kirdy):
|
||||||
"""
|
"""
|
||||||
Configure Kirdy to actively report status
|
Configure Kirdy to actively report status to connected socket
|
||||||
Press Ctrl + C to exit active report mode
|
Press Ctrl + C to exit active report mode
|
||||||
"""
|
"""
|
||||||
def active_report(kirdy: Kirdy):
|
async for data in kirdy.report_mode():
|
||||||
class SignalHandler:
|
|
||||||
def __init__(self):
|
|
||||||
signal.signal(signal.SIGINT, self.exit_gracefully)
|
|
||||||
signal.signal(signal.SIGTERM, self.exit_gracefully)
|
|
||||||
def exit_gracefully(self, signum, frame):
|
|
||||||
kirdy.stop_report_mode()
|
|
||||||
|
|
||||||
signal_handler = SignalHandler()
|
|
||||||
|
|
||||||
for data in kirdy.get_report_stream():
|
|
||||||
pp(data)
|
pp(data)
|
||||||
|
|
||||||
|
async def device_cfg(kirdy: Kirdy):
|
||||||
"""
|
"""
|
||||||
Configure Kirdy network and board specific transconductance settings.
|
Configure Kirdy's network and board specific transconductance settings.
|
||||||
These configs are saved to flash immediately after command is processed.
|
These configs are saved to flash immediately after command is processed.
|
||||||
"""
|
"""
|
||||||
def device_cfg(kirdy: Kirdy):
|
|
||||||
# Kirdy rev0_3's gain and transconductance varies between boards to maximize the
|
# Kirdy rev0_3's gain and transconductance varies between boards to maximize the
|
||||||
# PD current range resolution.
|
# PD current range resolution.
|
||||||
kirdy.device.set_pd_mon_fin_gain(1.0)
|
await kirdy.device.set_pd_mon_fin_gain(1.0)
|
||||||
kirdy.device.set_pd_mon_transconductance(1/1000.0)
|
await kirdy.device.set_pd_mon_transconductance(1/1000.0)
|
||||||
|
|
||||||
# Network Settings will be updated on next reboot.
|
# Network Settings will be updated on next reboot.
|
||||||
kirdy.device.set_ip_settings(
|
await kirdy.device.set_ip_settings(
|
||||||
addr="192.168.1.128",
|
addr="192.168.1.128",
|
||||||
port=1337,
|
port=1337,
|
||||||
prefix_len=24,
|
prefix_len=24,
|
||||||
gateway="192.168.1.1"
|
gateway="192.168.1.1"
|
||||||
)
|
)
|
||||||
# Hard reset Kirdy.
|
# Hard reset Kirdy.
|
||||||
kirdy.device.hard_reset()
|
await kirdy.device.hard_reset()
|
||||||
|
|
||||||
|
async def ld_thermostat_cfg(kirdy: Kirdy):
|
||||||
"""
|
"""
|
||||||
Control and config laser diode and thermostat parameters.
|
Control and config laser diode and thermostat parameters.
|
||||||
"""
|
"""
|
||||||
def ld_thermostat_cfg(kirdy: Kirdy):
|
# Load the laser diode and thermostat settings from flash memory.
|
||||||
# Load the laser diode and thermostat settings from flash
|
await kirdy.device.restore_settings_from_flash()
|
||||||
kirdy.device.restore_settings_from_flash()
|
|
||||||
|
|
||||||
# Power off the laser diode and thermostat and clear alarm (if any)
|
# Power off the laser diode & thermostat and clear any asserted alarm
|
||||||
kirdy.laser.set_power_on(False)
|
await kirdy.laser.set_power_on(False)
|
||||||
kirdy.laser.clear_alarm()
|
await kirdy.laser.clear_alarm()
|
||||||
kirdy.thermostat.set_power_on(False)
|
await kirdy.thermostat.set_power_on(False)
|
||||||
kirdy.thermostat.clear_alarm()
|
await kirdy.thermostat.clear_alarm()
|
||||||
|
|
||||||
# Set the laser diode terminals not to be shorted
|
# Set the laser diode terminals not to be shorted
|
||||||
kirdy.laser.set_ld_terms_short(False)
|
await kirdy.laser.set_ld_terms_short(False)
|
||||||
|
|
||||||
# Do not power up the laser & thermostat during initial startup
|
# Do not power up the laser & thermostat during initial startup
|
||||||
kirdy.laser.set_default_pwr_on(False)
|
await kirdy.laser.set_default_pwr_on(False)
|
||||||
kirdy.thermostat.set_default_pwr_on(False)
|
await kirdy.thermostat.set_default_pwr_on(False)
|
||||||
|
|
||||||
kirdy.laser.set_i(0)
|
await kirdy.laser.set_i(0)
|
||||||
|
|
||||||
# Configure the laser diode output power limit and photodiode parameters
|
# Configure the laser diode output power limit and photodiode parameters
|
||||||
# Exceeding the measured power limit triggers overpower protection alarm.
|
# Exceeding the power limit triggers overpower protection alarm.
|
||||||
# The laser diode power will be turned off while the thermostat power remains unchanged.
|
# The laser diode power will be cut off upon alarm assertion while the thermostat power remains unchanged.
|
||||||
kirdy.laser.set_ld_pwr_limit(0.0)
|
await kirdy.laser.set_ld_pwr_limit(0.0)
|
||||||
kirdy.laser.set_pd_mon_dark_current(0.0)
|
await kirdy.laser.set_pd_mon_dark_current(0.0)
|
||||||
kirdy.laser.set_pd_mon_responsitivity(0.0)
|
await kirdy.laser.set_pd_mon_responsitivity(0.0)
|
||||||
|
|
||||||
# Configure the thermostat NTC thermistor parameters.
|
# Configure the thermostat NTC thermistor parameters.
|
||||||
kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||||
kirdy.thermostat.set_sh_t0(25)
|
await kirdy.thermostat.set_sh_t0(25)
|
||||||
kirdy.thermostat.set_sh_beta(3900)
|
await kirdy.thermostat.set_sh_beta(3900)
|
||||||
|
|
||||||
# Configure the thermostat output limits.
|
# Configure the thermostat TEC settings.
|
||||||
# The actual output current is limited by the hardware limit set below.
|
# The actual output current is limited by value set below.
|
||||||
kirdy.thermostat.set_tec_max_cooling_i(1.0)
|
await kirdy.thermostat.set_tec_max_cooling_i(1.0)
|
||||||
kirdy.thermostat.set_tec_max_heating_i(1.0)
|
await kirdy.thermostat.set_tec_max_heating_i(1.0)
|
||||||
kirdy.thermostat.set_tec_max_v(4.0)
|
await kirdy.thermostat.set_tec_max_v(4.0)
|
||||||
|
|
||||||
# Configure the thermostat temperature monitor limit.
|
# Configure the thermostat temperature monitor limits.
|
||||||
# Exceeding the limit will trigger over temperature protection alarm.
|
# Exceeding the temperature limits trigger over temperature protection alarm.
|
||||||
# The laser diode and thermostat power will be turned off.
|
# Both laser diode and thermostat power will be cut off upon alarm assertion.
|
||||||
kirdy.thermostat.set_temp_mon_upper_limit(70)
|
await kirdy.thermostat.set_temp_mon_upper_limit(70)
|
||||||
kirdy.thermostat.set_temp_mon_lower_limit(0)
|
await kirdy.thermostat.set_temp_mon_lower_limit(0)
|
||||||
|
|
||||||
# Configure the thermostat PID related parameter.
|
# Configure the thermostat PID parameters.
|
||||||
# You can configure the PID parameter with the autotune tool.
|
# You can configure the PID parameter by the included autotune script.
|
||||||
# Here provides an example if it is configured manually.
|
await kirdy.thermostat.set_temperature_setpoint(25)
|
||||||
kirdy.thermostat.set_temperature_setpoint(25)
|
await kirdy.thermostat.set_pid_kp(0.15668282198105507)
|
||||||
kirdy.thermostat.set_pid_kp(0.15668282198105507)
|
await kirdy.thermostat.set_pid_ki(0.002135962407793784)
|
||||||
kirdy.thermostat.set_pid_ki(0.002135962407793784)
|
await kirdy.thermostat.set_pid_kd(0.829254515277143)
|
||||||
kirdy.thermostat.set_pid_kd(0.829254515277143)
|
await kirdy.thermostat.set_pid_output_max(1.0)
|
||||||
kirdy.thermostat.set_pid_output_max(1.0)
|
await kirdy.thermostat.set_pid_output_min(-1.0)
|
||||||
kirdy.thermostat.set_pid_output_min(-1.0)
|
|
||||||
|
|
||||||
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
|
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
|
||||||
# The ADC sampling rate determines the report and pid update rate.
|
# The ADC sampling rate determines the report and pid update rate.
|
||||||
# The chosen filter and sampling rate affects the noise of the readings.
|
# The chosen filter and sampling rate affects the noise of the readings.
|
||||||
# For details, please refer to the AD7172 da`tas`heet.
|
# For details, please refer to the AD7172 datasheet.
|
||||||
kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||||
|
|
||||||
# Configure thermostat to run in PID control mode
|
# Configure thermostat to run in PID control mode
|
||||||
kirdy.thermostat.set_pid_control_mode()
|
await kirdy.thermostat.set_pid_control_mode()
|
||||||
|
|
||||||
# When control mode is switched from PID to constant current(CC) control mode,
|
# When control mode is switched from PID to constant current(CC) control mode,
|
||||||
# the thermostat keeps its instantaneous output current unchanged.
|
# thermostat keeps its instantaneous output current unchanged.
|
||||||
# Thermostat output current should only be set if it is in CC control mode
|
# Thermostat output current should only be set if it is in CC control mode
|
||||||
# or the value set will not be overwritten by PID output.
|
# or the value set will be overwritten by PID output.
|
||||||
kirdy.thermostat.set_constant_current_control_mode()
|
await kirdy.thermostat.set_constant_current_control_mode()
|
||||||
kirdy.thermostat.set_tec_i_out(0.0)
|
await kirdy.thermostat.set_tec_i_out(0.0)
|
||||||
|
|
||||||
# Save the above settings configured into the flash
|
# Save the current settings to flash memory
|
||||||
kirdy.device.save_current_settings_to_flash()
|
await kirdy.device.save_current_settings_to_flash()
|
||||||
|
|
||||||
# Power on the laser diode and thermostat
|
# Power on the laser diode and thermostat
|
||||||
kirdy.laser.set_power_on(True)
|
await kirdy.laser.set_power_on(True)
|
||||||
kirdy.thermostat.set_power_on(True)
|
await kirdy.thermostat.set_power_on(True)
|
||||||
|
|
||||||
pp(kirdy.device.get_settings_summary())
|
pp(await kirdy.device.get_settings_summary())
|
||||||
pp(kirdy.device.get_status_report())
|
pp(await kirdy.device.get_status_report())
|
||||||
|
|
||||||
def main():
|
async def main():
|
||||||
kirdy = Kirdy()
|
kirdy = Kirdy()
|
||||||
kirdy.start_session(host='192.168.1.128', port=1337)
|
kirdy.start_session(host='192.168.1.128', port=1337)
|
||||||
|
await kirdy.wait_until_connected()
|
||||||
|
|
||||||
while not(kirdy.connected()):
|
await ld_thermostat_cfg(kirdy)
|
||||||
pass
|
# await active_report(kirdy)
|
||||||
|
|
||||||
ld_thermostat_cfg(kirdy)
|
|
||||||
# active_report(kirdy)
|
|
||||||
# await device_cfg(kirdy)
|
# await device_cfg(kirdy)
|
||||||
# enter_dfu_mode(kirdy)
|
# await enter_dfu_mode(kirdy)
|
||||||
|
|
||||||
kirdy.end_session(block=True)
|
await kirdy.end_session()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
asyncio.run(main())
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import types
|
||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
@ -25,6 +26,7 @@ class CmdList:
|
||||||
SetPdFinGain = _dt.f32
|
SetPdFinGain = _dt.f32
|
||||||
SetPdTransconductance = _dt.f32
|
SetPdTransconductance = _dt.f32
|
||||||
SetActiveReportMode = _dt.bool
|
SetActiveReportMode = _dt.bool
|
||||||
|
GetHwRev = _dt.none
|
||||||
GetStatusReport = _dt.none
|
GetStatusReport = _dt.none
|
||||||
GetSettingsSummary = _dt.none
|
GetSettingsSummary = _dt.none
|
||||||
Dfu = _dt.none
|
Dfu = _dt.none
|
||||||
|
@ -153,15 +155,14 @@ class InvalidCmd(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Device:
|
class Device:
|
||||||
def __init__(self, send_cmd_handler, send_raw_cmd_handler, read_msg_queue):
|
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
|
||||||
self._cmd = CmdList.device
|
self._cmd = CmdList.device
|
||||||
self._send_cmd = send_cmd_handler
|
self._send_cmd = send_cmd_handler
|
||||||
self._send_raw_cmd = send_raw_cmd_handler
|
self._send_raw_cmd = send_raw_cmd_handler
|
||||||
self._read_msg_queue = read_msg_queue
|
|
||||||
|
|
||||||
def set_ip_settings(self, addr="192.168.1.128", port=1337, prefix_len=24, gateway="192.168.1.1"):
|
async def set_ip_settings(self, addr="192.168.1.128", port=1337, prefix_len=24, gateway="192.168.1.1"):
|
||||||
"""
|
"""
|
||||||
After calling this fn, the ip settings are immediately saved into flash and will be effective on next reboot.
|
Upon command execution, the ip settings are saved into flash and are effective upon next reboot.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
socket.inet_aton(addr)
|
socket.inet_aton(addr)
|
||||||
|
@ -175,7 +176,7 @@ class Device:
|
||||||
if not(isinstance(port, int) and isinstance(prefix_len, int)):
|
if not(isinstance(port, int) and isinstance(prefix_len, int)):
|
||||||
raise InvalidDataType
|
raise InvalidDataType
|
||||||
|
|
||||||
return self._send_raw_cmd(
|
return await self._send_raw_cmd(
|
||||||
{
|
{
|
||||||
"device_cmd": "SetIPSettings",
|
"device_cmd": "SetIPSettings",
|
||||||
"ip_settings": {
|
"ip_settings": {
|
||||||
|
@ -187,32 +188,45 @@ class Device:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_active_report_mode(self, on):
|
async def set_active_report_mode(self, on):
|
||||||
"""
|
"""
|
||||||
Set active report to be on. If it is on, Kirdy will send status report
|
Set active report to be on. If it is on, Kirdy will send status report
|
||||||
to ALL client socket connections according to the temperature polling rate set.
|
to the client socket according to the temperature polling rate set.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetActiveReportMode, on)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetActiveReportMode, on)
|
||||||
|
|
||||||
def set_pd_mon_fin_gain(self, gain):
|
async def set_pd_mon_fin_gain(self, gain):
|
||||||
"""
|
"""
|
||||||
Configure the photodiode monitor final analog front-end stage gain
|
Configure the photodiode monitor final analog front-end stage gain.
|
||||||
- gain: unitless
|
- gain: unitless
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPdFinGain, gain)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPdFinGain, gain)
|
||||||
|
|
||||||
def set_pd_mon_transconductance(self, transconductance):
|
async def set_pd_mon_transconductance(self, transconductance):
|
||||||
"""
|
"""
|
||||||
Configure the photodiode monitor transconductance
|
Configure the photodiode monitor transconductance value.
|
||||||
- transconductance: 1/Ohm
|
- transconductance: 1/Ohm
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPdTransconductance, transconductance)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPdTransconductance, transconductance)
|
||||||
|
|
||||||
def get_status_report(self, sig=None):
|
async def get_hw_rev(self):
|
||||||
"""
|
"""
|
||||||
Get status of all peripherals in a json object
|
Get hardware revision of the connected Kirdy
|
||||||
|
|
||||||
|
{
|
||||||
|
'msg_type': 'HwRev',
|
||||||
|
'hw_rev': {
|
||||||
|
'major': 0,
|
||||||
|
'minor': 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
return await self._send_cmd(self._cmd._target, self._cmd.GetHwRev, msg_type="HwRev")
|
||||||
|
|
||||||
|
async def get_status_report(self, sig=None):
|
||||||
|
"""
|
||||||
|
Get status of all peripherals in a json object.
|
||||||
|
|
||||||
Example of yielded data::
|
|
||||||
{
|
{
|
||||||
'ts': 227657, # Relative Timestamp (ms)
|
'ts': 227657, # Relative Timestamp (ms)
|
||||||
'msg_type': 'Report' # Indicate it is a 'Report' json object
|
'msg_type': 'Report' # Indicate it is a 'Report' json object
|
||||||
|
@ -222,13 +236,17 @@ class Device:
|
||||||
'ld_i_set': 0.0, # Laser Diode Output Current (A)
|
'ld_i_set': 0.0, # Laser Diode Output Current (A)
|
||||||
'pd_i': 2.0000002e-06, # Internal Photodiode Monitor current (A)
|
'pd_i': 2.0000002e-06, # Internal Photodiode Monitor current (A)
|
||||||
'pd_pwr': None, # Power Readings from Internal Photodiode (W). Return None if pd_mon parameter(s) are not defined.
|
'pd_pwr': None, # Power Readings from Internal Photodiode (W). Return None if pd_mon parameter(s) are not defined.
|
||||||
'term_50ohm': 'Is50Ohm' # Is the Low Frequency Modulation Input's Impedance 50 Ohm? (On/Off)
|
'term_50ohm': 'On' # Is the Low Frequency Modulation Input's Impedance 50 Ohm? ("On"/"Off")
|
||||||
},
|
},
|
||||||
'thermostat': {
|
'thermostat': {
|
||||||
'pwr_on': False, # Tec Power is On (True/False)
|
'pwr_on': False, # Tec Power is On (True/False)
|
||||||
'pid_engaged': False, # Is Pid_Engaged. If False, it is in Constant Current Mode (True/False)
|
'pid_engaged': False, # Is Pid_Engaged. If False, it is in Constant Current Mode (True/False)
|
||||||
'temp_mon_status': { # Temperature Monitor:
|
'temp_mon_status': { # Temperature Monitor:
|
||||||
'status': 'Off', # (To be revised)
|
'status': 'Off', # "Off": Power is Off
|
||||||
|
# "ConstantCurrentMode": Thermostat is regulated in CC mode
|
||||||
|
# "PidStartUp": PID Regulation is not stable
|
||||||
|
# "PidStable": PID Regulation is stable and the temperature is within +-1mK to the setpoint
|
||||||
|
# "OverTempAlarm": Overtemperature Alarm is triggered
|
||||||
'over_temp_alarm': False # Was Laser Diode experienced an Overtemperature condition (True/False)
|
'over_temp_alarm': False # Was Laser Diode experienced an Overtemperature condition (True/False)
|
||||||
},
|
},
|
||||||
'temperature': 25.03344, # Temperature Readings (Degree Celsius)
|
'temperature': 25.03344, # Temperature Readings (Degree Celsius)
|
||||||
|
@ -238,15 +256,11 @@ class Device:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if sig is None:
|
return await self._send_cmd(self._cmd._target, self._cmd.GetStatusReport, msg_type="Report", sig=sig)
|
||||||
self._send_cmd(self._cmd._target, self._cmd.GetStatusReport, msg_type="Report", sig=sig)
|
|
||||||
return self._read_msg_queue()
|
|
||||||
else:
|
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.GetStatusReport, msg_type="Report", sig=sig)
|
|
||||||
|
|
||||||
def get_settings_summary(self, sig=None):
|
async def get_settings_summary(self, sig=None):
|
||||||
"""
|
"""
|
||||||
Get the current settings of laser and thermostat in a json object
|
Get the current settings of laser and thermostat in a json object.
|
||||||
|
|
||||||
{
|
{
|
||||||
'msg_type': 'Settings', # Indicate it is a 'Settings' json object
|
'msg_type': 'Settings', # Indicate it is a 'Settings' json object
|
||||||
|
@ -255,13 +269,13 @@ class Device:
|
||||||
'ld_drive_current': { # Laser Diode Output Current(A)
|
'ld_drive_current': { # Laser Diode Output Current(A)
|
||||||
'value': 0.0, # Value Set
|
'value': 0.0, # Value Set
|
||||||
'max': 0.3 # Max Value Settable
|
'max': 0.3 # Max Value Settable
|
||||||
,
|
},
|
||||||
'pd_mon_params': { # Laser Diode Software Current Limit(A)
|
'pd_mon_params': { # Laser Diode Software Current Limit(A)
|
||||||
'responsitivity': None, # Value Set
|
'responsitivity': None, # Value Set
|
||||||
'i_dark': 0.0 # Max Value Settable
|
'i_dark': 0.0 # Max Value Settable
|
||||||
,
|
},
|
||||||
'ld_pwr_limit': 0.0 # Laser Diode Power Limit(W)
|
'ld_pwr_limit': 0.0, # Laser Diode Power Limit(W)
|
||||||
'ld_terms_short: False # Is Laser Diode Terminals short? (True/False)
|
'ld_terms_short: False, # Is Laser Diode Terminals short? (True/False)
|
||||||
},
|
},
|
||||||
'thermostat': {
|
'thermostat': {
|
||||||
'default_pwr_on': True, # Power on Thermostat at Startup
|
'default_pwr_on': True, # Power on Thermostat at Startup
|
||||||
|
@ -312,106 +326,101 @@ class Device:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if sig is None:
|
return await self._send_cmd(self._cmd._target, self._cmd.GetSettingsSummary, msg_type="Settings", sig=sig)
|
||||||
self._send_cmd(self._cmd._target, self._cmd.GetSettingsSummary, msg_type="Settings", sig=sig)
|
|
||||||
return self._read_msg_queue()
|
|
||||||
else:
|
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.GetSettingsSummary, msg_type="Settings", sig=sig)
|
|
||||||
|
|
||||||
def dfu(self):
|
async def dfu(self):
|
||||||
"""
|
"""
|
||||||
Issuing this cmd will HARD RESET the device and
|
Hard reset and put the connected Kirdy into the Dfu mode for firmware update.
|
||||||
put Kirdy into Dfu mode for flashing firmware.
|
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.Dfu, hard_reset=True)
|
return await self._send_cmd(self._cmd._target, self._cmd.Dfu)
|
||||||
|
|
||||||
def save_current_settings_to_flash(self):
|
async def save_current_settings_to_flash(self):
|
||||||
"""
|
"""
|
||||||
Save the current laser diode and thermostat configurations into flash.
|
Save the current laser diode and thermostat configurations into flash.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SaveFlashSettings)
|
return await self._send_cmd(self._cmd._target, self._cmd.SaveFlashSettings)
|
||||||
|
|
||||||
def restore_settings_from_flash(self):
|
async def restore_settings_from_flash(self):
|
||||||
"""
|
"""
|
||||||
Restore the laser diode and thermostat settings from flash
|
Restore the laser diode and thermostat settings from flash.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.LoadFlashSettings)
|
return await self._send_cmd(self._cmd._target, self._cmd.LoadFlashSettings)
|
||||||
|
|
||||||
def hard_reset(self):
|
async def hard_reset(self):
|
||||||
"""
|
"""
|
||||||
Hard Reset Kirdy. The socket connection will be closed by Kirdy.
|
Hard Reset Kirdy. The socket connection will be closed by Kirdy.
|
||||||
Laser diode power and Tec power will be turned off.
|
Laser diode power and Tec power will be turned off.
|
||||||
Kirdy will send out a json({'msg_type': 'HardReset'}) to all sockets indicating. The device is being reset.
|
Kirdy will send out a json({'msg_type': 'HardReset'}) to all sockets before hard reset take place.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.HardReset, hard_reset=True)
|
return await self._send_cmd(self._cmd._target, self._cmd.HardReset)
|
||||||
|
|
||||||
class Laser:
|
class Laser:
|
||||||
def __init__(self, send_cmd_handler):
|
def __init__(self, send_cmd_handler):
|
||||||
self._cmd = CmdList.ld
|
self._cmd = CmdList.ld
|
||||||
self._send_cmd = send_cmd_handler
|
self._send_cmd = send_cmd_handler
|
||||||
|
|
||||||
def set_power_on(self, on):
|
async def set_power_on(self, on):
|
||||||
"""
|
"""
|
||||||
Power Up or Power Down laser diode. Powering up the Laser Diode resets the pwr_excursion status
|
Power Up or Power Down laser diode. Powering up the Laser Diode resets the pwr_excursion status
|
||||||
- on (True/False)
|
- on (True/False)
|
||||||
"""
|
"""
|
||||||
if on:
|
if on:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
|
||||||
else:
|
else:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
|
||||||
|
|
||||||
def set_default_pwr_on(self, on):
|
async def set_default_pwr_on(self, on):
|
||||||
"""
|
"""
|
||||||
Set whether laser diode is powered up at Startup
|
Set whether laser diode is powered up at Startup.
|
||||||
- on (True/False)
|
- on (True/False)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
|
||||||
|
|
||||||
def set_ld_terms_short(self, short):
|
async def set_ld_terms_short(self, short):
|
||||||
"""
|
"""
|
||||||
Open/Short laser diode terminals.
|
Open/Short laser diode terminals.
|
||||||
- on (True/False)
|
- on (True/False)
|
||||||
"""
|
"""
|
||||||
if short:
|
if short:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.LdTermsShort, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.LdTermsShort, None)
|
||||||
else:
|
else:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.LdTermsOpen, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.LdTermsOpen, None)
|
||||||
|
|
||||||
def set_i(self, i):
|
async def set_i(self, i):
|
||||||
"""
|
"""
|
||||||
Set laser diode output current: Max(0, Min(i_set, i_soft_limit))
|
Set laser diode output current: Max(0, Min(i_set, 300mA)).
|
||||||
- i: A
|
- i: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetI, i)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetI, i)
|
||||||
|
|
||||||
def set_pd_mon_responsitivity(self, responsitivity):
|
async def set_pd_mon_responsitivity(self, responsitivity):
|
||||||
"""
|
"""
|
||||||
Configure the photodiode monitor responsitivity parameter
|
Configure the photodiode monitor responsitivity parameter.
|
||||||
- responsitivity: A/W
|
- responsitivity: A/W
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPdResponsitivity, responsitivity)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPdResponsitivity, responsitivity)
|
||||||
|
|
||||||
def set_pd_mon_dark_current(self, dark_current):
|
async def set_pd_mon_dark_current(self, dark_current):
|
||||||
"""
|
"""
|
||||||
Configure the photodiode monitor dark current parameter
|
Configure the photodiode monitor dark current parameter.
|
||||||
- dark_current: A
|
- dark_current: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPdDarkCurrent, dark_current)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPdDarkCurrent, dark_current)
|
||||||
|
|
||||||
def set_ld_pwr_limit(self, pwr_limit):
|
async def set_ld_pwr_limit(self, pwr_limit):
|
||||||
"""
|
"""
|
||||||
Set power limit for the power excursion monitor
|
Set the power limit for the power excursion monitor.
|
||||||
If the calculated power with the params of pd_mon > pwr_limit,
|
If the calculated power with the params of pd_mon > pwr_limit,
|
||||||
overpower protection is triggered.
|
overpower protection is triggered.
|
||||||
- pwr_limit: W
|
- pwr_limit: W
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetLdPwrLimit, pwr_limit)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetLdPwrLimit, pwr_limit)
|
||||||
|
|
||||||
def clear_alarm(self):
|
async def clear_alarm(self):
|
||||||
"""
|
"""
|
||||||
Clear the power excursion monitor alarm
|
Clear the power excursion monitor alarm.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
|
return await self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
|
||||||
|
|
||||||
class Thermostat:
|
class Thermostat:
|
||||||
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
|
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
|
||||||
|
@ -419,158 +428,158 @@ class Thermostat:
|
||||||
self._send_cmd = send_cmd_handler
|
self._send_cmd = send_cmd_handler
|
||||||
self._send_raw_cmd = send_raw_cmd_handler
|
self._send_raw_cmd = send_raw_cmd_handler
|
||||||
|
|
||||||
def set_power_on(self, on):
|
async def set_power_on(self, on):
|
||||||
"""
|
"""
|
||||||
Power up or power down thermostat
|
Power up or power down thermostat.
|
||||||
- Powering up the thermostat resets the pwr_excursion status
|
- Powering up the thermostat resets the pwr_excursion status
|
||||||
"""
|
"""
|
||||||
if on:
|
if on:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
|
||||||
else:
|
else:
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
|
||||||
|
|
||||||
def set_default_pwr_on(self, on):
|
async def set_default_pwr_on(self, on):
|
||||||
"""
|
"""
|
||||||
Set whether thermostat is powered up at Startup
|
Set whether thermostat is powered up at Startup.
|
||||||
- on: (True/False)
|
- on: (True/False)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
|
||||||
|
|
||||||
def set_tec_max_v(self, max_v):
|
async def set_tec_max_v(self, max_v):
|
||||||
"""
|
"""
|
||||||
Set Tec Maximum Voltage Across the TEC Terminals
|
Set Tec Maximum Voltage Across the TEC Terminals.
|
||||||
- max_v: V
|
- max_v: V
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTecMaxV, max_v)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxV, max_v)
|
||||||
|
|
||||||
def set_tec_max_cooling_i(self, max_i_pos):
|
async def set_tec_max_cooling_i(self, max_i_pos):
|
||||||
"""
|
"""
|
||||||
Set Tec maximum cooling current (Settable Range: 0.0 - 1.0)
|
Set Tec maximum cooling current (Settable Range: 0.0 - 1.0)
|
||||||
- max_i_pos: A
|
- max_i_pos: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTecMaxIPos, max_i_pos)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxIPos, max_i_pos)
|
||||||
|
|
||||||
def set_tec_max_heating_i(self, max_i_neg):
|
async def set_tec_max_heating_i(self, max_i_neg):
|
||||||
"""
|
"""
|
||||||
Set Tec maximum heating current (Settable Range: 0.0 - 1.0)
|
Set Tec maximum heating current (Settable Range: 0.0 - 1.0)
|
||||||
- max_i_neg: A
|
- max_i_neg: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTecMaxINeg, max_i_neg)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxINeg, max_i_neg)
|
||||||
|
|
||||||
def set_tec_i_out(self, i_out):
|
async def set_tec_i_out(self, i_out):
|
||||||
"""
|
"""
|
||||||
Set Tec Output Current
|
Set Tec Output Current (Settable Range: 0.0 - 1.0)
|
||||||
This cmd is only effective in constant current control mode
|
This cmd is only effective in constant current control mode
|
||||||
or your newly set value will be overwritten by PID Controller Output
|
or your newly set value will be overwritten by PID Controller Output
|
||||||
- i_out: A
|
- i_out: A
|
||||||
"""
|
"""
|
||||||
if isinstance(i_out, float):
|
if isinstance(i_out, float):
|
||||||
return self._send_raw_cmd({"tec_set_i": i_out})
|
return await self._send_raw_cmd({"tec_set_i": i_out})
|
||||||
elif isinstance(i_out, int):
|
elif isinstance(i_out, int):
|
||||||
return self._send_raw_cmd({"tec_set_i": float(i_out)})
|
return await self._send_raw_cmd({"tec_set_i": float(i_out)})
|
||||||
else:
|
else:
|
||||||
raise InvalidDataType
|
raise InvalidDataType
|
||||||
|
|
||||||
def set_constant_current_control_mode(self):
|
async def set_constant_current_control_mode(self):
|
||||||
"""
|
"""
|
||||||
Disable PID Controller and output current can be controlled with set_tec_i_out() cmd.
|
Disable PID Controller and output current can be controlled with set_tec_i_out() cmd.
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidDisEngage, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidDisEngage, None)
|
||||||
|
|
||||||
def set_temperature_setpoint(self, temperature):
|
async def set_temperature_setpoint(self, temperature):
|
||||||
"""
|
"""
|
||||||
Set Temperature Setpoint for PID Controller. This parameter is not active in constant current control mode
|
Set Temperature Setpoint for PID Controller. This parameter is not active in constant current control mode
|
||||||
- temperature: Degree Celsius
|
- temperature: Degree Celsius
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTemperatureSetpoint, temperature)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTemperatureSetpoint, temperature)
|
||||||
|
|
||||||
def set_pid_control_mode(self):
|
async def set_pid_control_mode(self):
|
||||||
"""
|
"""
|
||||||
Enable PID Controller. Its PID Update Interval is controlled by the Temperature ADC polling rate.
|
Enable PID Controller. Its PID Update Interval is controlled by the Temperature ADC polling rate.
|
||||||
Please refer to config_temp_adc_filter for the possible polling rate options
|
Please refer to config_temp_adc_filter for the possible polling rate options
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidEngage, None)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidEngage, None)
|
||||||
|
|
||||||
def set_pid_kp(self, kp):
|
async def set_pid_kp(self, kp):
|
||||||
"""
|
"""
|
||||||
Set Kp parameter for PID Controller
|
Set Kp parameter for PID Controller
|
||||||
kp: (unitless)
|
kp: (unitless)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidKp, kp)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKp, kp)
|
||||||
|
|
||||||
def set_pid_ki(self, ki):
|
async def set_pid_ki(self, ki):
|
||||||
"""
|
"""
|
||||||
Set Ki parameter for PID Controller
|
Set Ki parameter for PID Controller
|
||||||
ki: (unitless)
|
ki: (unitless)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidKi, ki)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKi, ki)
|
||||||
|
|
||||||
def set_pid_kd(self, kd):
|
async def set_pid_kd(self, kd):
|
||||||
"""
|
"""
|
||||||
Set Kd parameter for PID Controller
|
Set Kd parameter for PID Controller
|
||||||
kd: (unitless)
|
kd: (unitless)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidKd, kd)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKd, kd)
|
||||||
|
|
||||||
def set_pid_output_max(self, out_max):
|
async def set_pid_output_max(self, out_max):
|
||||||
"""
|
"""
|
||||||
Set max output limit at the PID Output
|
Set max output limit at the PID Output
|
||||||
- out_max: A
|
- out_max: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidOutMax, out_max)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidOutMax, out_max)
|
||||||
|
|
||||||
def set_pid_output_min(self, out_min):
|
async def set_pid_output_min(self, out_min):
|
||||||
"""
|
"""
|
||||||
Set min output limit at the PID Output
|
Set min output limit at the PID Output
|
||||||
- out_min: A
|
- out_min: A
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetPidOutMin, out_min)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetPidOutMin, out_min)
|
||||||
|
|
||||||
def set_temp_mon_upper_limit(self, upper_limit):
|
async def set_temp_mon_upper_limit(self, upper_limit):
|
||||||
"""
|
"""
|
||||||
Set Temperature Monitor Upper Limit Threshold. Exceeding the limit for too long
|
Set Temperature Monitor Upper Limit Threshold. Exceeding the limit for too long
|
||||||
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
||||||
- upper_limit: Degree Celsius
|
- upper_limit: Degree Celsius
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTempMonUpperLimit, upper_limit)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTempMonUpperLimit, upper_limit)
|
||||||
|
|
||||||
def set_temp_mon_lower_limit(self, lower_limit):
|
async def set_temp_mon_lower_limit(self, lower_limit):
|
||||||
"""
|
"""
|
||||||
Set Temperature Monitor Lower Limit Threshold. Exceeding the limit for too long
|
Set Temperature Monitor Lower Limit Threshold. Exceeding the limit for too long
|
||||||
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
||||||
- lower_limit: Degree Celsius
|
- lower_limit: Degree Celsius
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetTempMonLowerLimit, lower_limit)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetTempMonLowerLimit, lower_limit)
|
||||||
|
|
||||||
def clear_alarm(self):
|
async def clear_alarm(self):
|
||||||
"""
|
"""
|
||||||
Clear the temperature monitor alarm
|
Clear the temperature monitor alarm
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
|
return await self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
|
||||||
|
|
||||||
def set_sh_t0(self, t0):
|
async def set_sh_t0(self, t0):
|
||||||
"""
|
"""
|
||||||
Set t0 Steinhart-Hart parameter for the laser diode NTC
|
Set t0 Steinhart-Hart parameter for the laser diode NTC
|
||||||
- t0: Degree Celsius
|
- t0: Degree Celsius
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetShT0, t0)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetShT0, t0)
|
||||||
|
|
||||||
def set_sh_r0(self, r0):
|
async def set_sh_r0(self, r0):
|
||||||
"""
|
"""
|
||||||
Set r0 Steinhart-Hart parameter for the laser diode NTC
|
Set r0 Steinhart-Hart parameter for the laser diode NTC
|
||||||
- r0: Ohm
|
- r0: Ohm
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetShR0, r0)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetShR0, r0)
|
||||||
|
|
||||||
def set_sh_beta(self, beta):
|
async def set_sh_beta(self, beta):
|
||||||
"""
|
"""
|
||||||
Set beta Steinhart-Hart parameter for the laser diode NTC
|
Set beta Steinhart-Hart parameter for the laser diode NTC
|
||||||
- beta: (unitless)
|
- beta: (unitless)
|
||||||
"""
|
"""
|
||||||
return self._send_cmd(self._cmd._target, self._cmd.SetShBeta, beta)
|
return await self._send_cmd(self._cmd._target, self._cmd.SetShBeta, beta)
|
||||||
|
|
||||||
def config_temp_adc_filter(self, filter_config):
|
async def config_temp_adc_filter(self, filter_config):
|
||||||
"""
|
"""
|
||||||
Configure the temperature adc filter type and sampling rate.
|
Configure the temperature adc filter type and sampling rate.
|
||||||
Please refer to AD7172 datasheet for the usage of various types of filter.
|
Please refer to AD7172 datasheet for the usage of various types of filter.
|
||||||
|
@ -591,29 +600,38 @@ class Thermostat:
|
||||||
filter_config._odr_type(): filter_config,
|
filter_config._odr_type(): filter_config,
|
||||||
}
|
}
|
||||||
|
|
||||||
return self._send_raw_cmd(cmd)
|
return await self._send_raw_cmd(cmd)
|
||||||
|
|
||||||
class Kirdy:
|
class Kirdy:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.device = Device(self._send_cmd_handler, self._send_raw_cmd_handler, self._get_msg)
|
self.device = Device(self._send_cmd, self._send_raw_cmd)
|
||||||
self.laser = Laser(self._send_cmd_handler)
|
self.laser = Laser(self._send_cmd)
|
||||||
self.thermostat = Thermostat(self._send_cmd_handler, self._send_raw_cmd_handler)
|
self.thermostat = Thermostat(self._send_cmd, self._send_raw_cmd)
|
||||||
|
self.hw_rev = None
|
||||||
|
|
||||||
self._task_queue, self._msg_queue, self._int_msg_queue, self._report_queue = None, None, None, None
|
self._task_queue, self._int_msg_queue, self._report_queue = None, None, None
|
||||||
|
self._timeout = 5.0
|
||||||
self._writer, self._reader = None, None
|
self._writer, self._reader = None, None
|
||||||
self._event_loop = None
|
self._event_loop = None
|
||||||
|
|
||||||
self._lock = asyncio.Lock()
|
|
||||||
self._msg_queue_get_report = False
|
self._msg_queue_get_report = False
|
||||||
self._report_mode_on = False
|
self._report_mode_on = False
|
||||||
|
self._state = State.disconnected
|
||||||
|
|
||||||
|
self.read_response_task, self.handler_task = None, None
|
||||||
|
self._lock = asyncio.Lock()
|
||||||
|
|
||||||
# PyQt Signal
|
# PyQt Signal
|
||||||
self._report_sig = None # Dict
|
self._report_sig = None # Dict
|
||||||
self._connected_sig = None # Bool
|
self._connected_sig = None # Bool
|
||||||
|
|
||||||
|
self.connected_event = None
|
||||||
|
|
||||||
|
def get_hw_rev(self):
|
||||||
|
return self.hw_rev
|
||||||
|
|
||||||
def set_report_sig(self, sig):
|
def set_report_sig(self, sig):
|
||||||
"""
|
"""
|
||||||
Connect a PyQt Signal to the active report output(dict)
|
Connect a PyQt Signal to the active report output(dict). This should be configured before the session is started.
|
||||||
"""
|
"""
|
||||||
self._report_sig = sig
|
self._report_sig = sig
|
||||||
|
|
||||||
|
@ -625,40 +643,41 @@ class Kirdy:
|
||||||
"""
|
"""
|
||||||
self._connected_sig = sig
|
self._connected_sig = sig
|
||||||
|
|
||||||
def start_session(self, host='192.168.1.128', port=1337, timeout=5.0, con_retry=5.0):
|
def start_session(self, host='192.168.1.128', port=1337):
|
||||||
"""
|
"""
|
||||||
Start Kirdy Connection Session.
|
Start Kirdy Connection Session.
|
||||||
A new thread is started to handle TCP connection and task execution in the background.
|
In case of disconnection, all the queued tasks are cleared and the handler task retries TCP connection indefinitely.
|
||||||
In case of disconnection, all the queued tasks are cleared and the thread retries TCP connection indefinitely.
|
|
||||||
|
|
||||||
- host: Kirdy's IP Address
|
- host: Kirdy's IP Address
|
||||||
- port: Kirdy's TCP Port
|
- port: Kirdy's Port Number
|
||||||
"""
|
"""
|
||||||
if self._event_loop is None:
|
|
||||||
self._host, self._ctrl_port = host, port
|
self._host, self._ctrl_port = host, port
|
||||||
self._timeout, self._con_retry = timeout, con_retry
|
if self._event_loop is None:
|
||||||
|
try:
|
||||||
|
self._event_loop = asyncio.get_running_loop()
|
||||||
|
except:
|
||||||
self._event_loop = asyncio.new_event_loop()
|
self._event_loop = asyncio.new_event_loop()
|
||||||
self._thread = Thread(target=self._event_loop.run_forever)
|
self._event_loop.run_forever()
|
||||||
self._thread.start()
|
self.connected_event = asyncio.Event()
|
||||||
asyncio.run_coroutine_threadsafe(self._handler(), self._event_loop)
|
self.handler_task = asyncio.create_task(self._handler())
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logging.warning("Helper Thread has been started.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def end_session(self, block=False):
|
async def end_session(self, block=False):
|
||||||
"""
|
"""
|
||||||
Stop Kirdy's TCP connection and its associated thread.
|
Stop Kirdy's TCP connection and its associated thread.
|
||||||
"""
|
"""
|
||||||
if self._event_loop is not None:
|
if self._event_loop is not None:
|
||||||
if block:
|
if block:
|
||||||
while not(self._task_queue.empty()):
|
await self._task_queue.join()
|
||||||
pass
|
|
||||||
cancel_task = asyncio.run_coroutine_threadsafe(self._stop_handler(), self._event_loop)
|
if self.read_response_task is not None:
|
||||||
while not(cancel_task.done()):
|
self.read_response_task.cancel()
|
||||||
pass
|
await self.read_response_task
|
||||||
self._thread.join()
|
self.read_response_task = None
|
||||||
|
|
||||||
|
if self.handler_task is not None:
|
||||||
|
self.handler_task.cancel()
|
||||||
|
await self.handler_task
|
||||||
|
self.handler_task = None
|
||||||
|
|
||||||
self._writer = None
|
self._writer = None
|
||||||
|
|
||||||
if self._connected_sig is not None:
|
if self._connected_sig is not None:
|
||||||
|
@ -668,7 +687,7 @@ class Kirdy:
|
||||||
"""
|
"""
|
||||||
Return True if client is connecting
|
Return True if client is connecting
|
||||||
"""
|
"""
|
||||||
return not self.connected() and self._event_loop is not None
|
return self._state == State.disconnected and self.read_response_task is not None
|
||||||
|
|
||||||
def connected(self):
|
def connected(self):
|
||||||
"""
|
"""
|
||||||
|
@ -676,28 +695,43 @@ class Kirdy:
|
||||||
"""
|
"""
|
||||||
return self._writer is not None
|
return self._writer is not None
|
||||||
|
|
||||||
def get_report_stream(self):
|
async def wait_until_connected(self):
|
||||||
|
if not(self.connected()):
|
||||||
|
await self.connected_event.wait()
|
||||||
|
|
||||||
|
async def report_mode(self):
|
||||||
"""
|
"""
|
||||||
Start reporting device status in json object.
|
Enable and retrieve active report from Kirdy
|
||||||
"""
|
"""
|
||||||
if self.connected():
|
if self.connected():
|
||||||
self._report_mode_on = True
|
self._report_mode_on = True
|
||||||
|
await self.device.set_active_report_mode(True)
|
||||||
self.device.set_active_report_mode(True)
|
report = None
|
||||||
|
|
||||||
while self._report_mode_on:
|
while self._report_mode_on:
|
||||||
report = self._report_queue.get()
|
report = await self._report_queue.get()
|
||||||
if isinstance(report, Exception):
|
if not(isinstance(report, dict)):
|
||||||
raise report
|
self.stop_active_report()
|
||||||
|
else:
|
||||||
yield report
|
yield report
|
||||||
|
|
||||||
self.device.set_active_report_mode(False)
|
if isinstance(report, dict):
|
||||||
|
await self.device.set_active_report_mode(False)
|
||||||
else:
|
else:
|
||||||
raise ConnectionError
|
raise ConnectionError
|
||||||
|
|
||||||
def stop_report_mode(self):
|
def stop_report_mode(self):
|
||||||
self._report_mode_on = False
|
self._report_mode_on = False
|
||||||
|
|
||||||
|
def task_dispatcher(self, awaitable_fn):
|
||||||
|
"""
|
||||||
|
Enqueue a task to be handled by the handler.
|
||||||
|
"""
|
||||||
|
if self.connected():
|
||||||
|
self._task_queue.put_nowait(lambda: awaitable_fn)
|
||||||
|
else:
|
||||||
|
raise ConnectionError
|
||||||
|
|
||||||
async def _sock_disconnection_handling(self):
|
async def _sock_disconnection_handling(self):
|
||||||
# Reader needn't be closed
|
# Reader needn't be closed
|
||||||
try:
|
try:
|
||||||
|
@ -709,11 +743,6 @@ class Kirdy:
|
||||||
self._reader = None
|
self._reader = None
|
||||||
self._writer = None
|
self._writer = None
|
||||||
|
|
||||||
for i in range(self._msg_queue.maxsize):
|
|
||||||
if self._msg_queue.full():
|
|
||||||
self._msg_queue.get_nowait()
|
|
||||||
self._msg_queue.put_nowait(None)
|
|
||||||
|
|
||||||
for i in range(self._report_queue.maxsize):
|
for i in range(self._report_queue.maxsize):
|
||||||
if self._report_queue.full():
|
if self._report_queue.full():
|
||||||
self._report_queue.get_nowait()
|
self._report_queue.get_nowait()
|
||||||
|
@ -724,97 +753,93 @@ class Kirdy:
|
||||||
|
|
||||||
async def _handler(self):
|
async def _handler(self):
|
||||||
try:
|
try:
|
||||||
state = State.disconnected
|
self._state = State.disconnected
|
||||||
first_con = True
|
first_con = True
|
||||||
task = None
|
task = None
|
||||||
while True:
|
while True:
|
||||||
if state == State.disconnected:
|
if self._state == State.disconnected:
|
||||||
try:
|
try:
|
||||||
|
self.hw_rev = None
|
||||||
await self.__coninit(self._timeout)
|
await self.__coninit(self._timeout)
|
||||||
read_response_fut = asyncio.run_coroutine_threadsafe(self._read_response_handler(), self._event_loop)
|
self.read_response_task = asyncio.create_task(self._read_response_handler())
|
||||||
task = None
|
task = None
|
||||||
logging.debug("Connected")
|
logging.info("Connected to %s:%d", self._host, self._ctrl_port)
|
||||||
|
|
||||||
|
hw_rev = await self.device.get_hw_rev()
|
||||||
|
self.hw_rev = hw_rev["hw_rev"]
|
||||||
|
|
||||||
|
if self._connected_sig is not None:
|
||||||
|
self._connected_sig.emit(True)
|
||||||
|
self.connected_event.set()
|
||||||
|
|
||||||
# State Transition
|
# State Transition
|
||||||
state = State.connected
|
self._state = State.connected
|
||||||
except (OSError, TimeoutError):
|
except (OSError, TimeoutError):
|
||||||
if first_con:
|
if first_con:
|
||||||
first_con = False
|
first_con = False
|
||||||
logging.warning("Cannot connect to %s:%d. Retrying in the background", self._host, self._ctrl_port)
|
logging.warning("Cannot connect to %s:%d. Retrying in the background.", self._host, self._ctrl_port)
|
||||||
await asyncio.sleep(self._con_retry)
|
await asyncio.sleep(5.0)
|
||||||
|
|
||||||
elif state == State.connected:
|
elif self._state == State.connected:
|
||||||
try:
|
try:
|
||||||
task = await self._task_queue.get()
|
task = await self._task_queue.get()
|
||||||
if isinstance(task, Exception):
|
if isinstance(task, Exception):
|
||||||
raise task
|
raise task
|
||||||
|
await task()
|
||||||
|
self._task_queue.task_done()
|
||||||
|
|
||||||
response = await asyncio.wait_for(task(), self._timeout)
|
except (TimeoutError, ConnectionResetError, ConnectionError):
|
||||||
if response is not None:
|
logging.warning("Connection to Kirdy is dropped.")
|
||||||
if response["msg_type"] != "Acknowledge":
|
|
||||||
self._msg_queue.put_nowait(response)
|
|
||||||
|
|
||||||
except (TimeoutError, ConnectionResetError):
|
|
||||||
logging.warning("Kirdy connection is dropped.")
|
|
||||||
first_con = True
|
first_con = True
|
||||||
read_response_fut.cancel()
|
self.read_response_task.cancel()
|
||||||
await self._sock_disconnection_handling()
|
|
||||||
|
|
||||||
# State Transition
|
# State Transition
|
||||||
state = State.disconnected
|
self._state = State.disconnected
|
||||||
|
await self._sock_disconnection_handling()
|
||||||
except asyncio.exceptions.CancelledError:
|
except asyncio.exceptions.CancelledError:
|
||||||
logging.debug("Handler is canceling")
|
pass
|
||||||
except:
|
except:
|
||||||
logging.debug("Handler experienced an error. Exiting.", exc_info=True)
|
logging.warning("Handler experienced an error.", exc_info=True)
|
||||||
await self._sock_disconnection_handling()
|
await self._sock_disconnection_handling()
|
||||||
if self._event_loop is not None:
|
|
||||||
self._event_loop.call_soon_threadsafe(self._event_loop.stop)
|
|
||||||
self._event_loop = None
|
|
||||||
|
|
||||||
async def _read_response_handler(self):
|
async def _read_response_handler(self):
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if self._report_mode_on:
|
if self._report_mode_on:
|
||||||
responses = await asyncio.wait_for(self._read_response(), 5.0)
|
response = await asyncio.wait_for(self._read_response(), self._timeout)
|
||||||
else:
|
else:
|
||||||
responses = await self._read_response()
|
response = await self._read_response()
|
||||||
for response in responses:
|
|
||||||
|
if response["msg_type"] == 'HardReset':
|
||||||
|
logging.warn("Kirdy is being hard reset.")
|
||||||
|
raise asyncio.exceptions.CancelledError
|
||||||
|
if response["msg_type"] == 'Dfu':
|
||||||
|
logging.warn("Kirdy enters Dfu Mode.")
|
||||||
|
asyncio.create_task(self.end_session())
|
||||||
|
if response["msg_type"] == 'ConnectionClose':
|
||||||
|
logging.warn("Kirdy runs out of TCP sockets and closes this connected socket.")
|
||||||
|
asyncio.create_task(self.end_session())
|
||||||
if response["msg_type"] == 'Report' and not self._msg_queue_get_report:
|
if response["msg_type"] == 'Report' and not self._msg_queue_get_report:
|
||||||
if self._report_sig is None:
|
if self._report_sig is None:
|
||||||
if self._report_queue.full():
|
self._report_queue.put_nowait_overwrite(response)
|
||||||
self._report_queue.get_nowait()
|
|
||||||
self._report_queue.put_nowait(response)
|
|
||||||
else:
|
else:
|
||||||
self._report_sig.emit(response)
|
self._report_sig.emit(response)
|
||||||
else:
|
else:
|
||||||
if self._msg_queue_get_report:
|
if self._msg_queue_get_report:
|
||||||
async with self._lock:
|
|
||||||
self._msg_queue_get_report = False
|
self._msg_queue_get_report = False
|
||||||
if self._int_msg_queue.full():
|
self._int_msg_queue.put_nowait_overwrite(response)
|
||||||
logging.debug("_int_msg_queue is full")
|
|
||||||
self._int_msg_queue.get_nowait()
|
|
||||||
self._int_msg_queue.put_nowait(response)
|
|
||||||
except asyncio.exceptions.CancelledError:
|
except asyncio.exceptions.CancelledError:
|
||||||
logging.debug("Read Response Handler is canceling")
|
pass
|
||||||
except TimeoutError:
|
except (TimeoutError, ConnectionResetError, ConnectionError) as exec:
|
||||||
logging.warning("Read active report response timeout")
|
self._task_queue.put_nowait_overwrite(exec)
|
||||||
if self._task_queue.full():
|
self._int_msg_queue.put_nowait_overwrite(exec)
|
||||||
logging.debug("_int_msg_queue is full")
|
except Exception as exec:
|
||||||
self._task_queue.get_nowait()
|
|
||||||
self._task_queue.put_nowait(TimeoutError())
|
|
||||||
except ConnectionResetError:
|
|
||||||
logging.warning("Connection Reset by peer")
|
|
||||||
if self._task_queue.full():
|
|
||||||
logging.debug("_int_msg_queue is full")
|
|
||||||
self._task_queue.get_nowait()
|
|
||||||
self._task_queue.put_nowait(ConnectionResetError())
|
|
||||||
except:
|
|
||||||
logging.warn("Read Response Handler experienced an error. Exiting.", exc_info=True)
|
logging.warn("Read Response Handler experienced an error. Exiting.", exc_info=True)
|
||||||
|
self._task_queue.put_nowait_overwrite(exec)
|
||||||
|
self._int_msg_queue.put_nowait_overwrite(exec)
|
||||||
if self._report_mode_on:
|
if self._report_mode_on:
|
||||||
self._report_mode_on = False
|
self._report_mode_on = False
|
||||||
self._report_queue.put_nowait(TimeoutError)
|
self._report_queue.put_nowait_overwrite(TimeoutError)
|
||||||
|
|
||||||
async def _stop_handler(self):
|
async def _stop_handler(self):
|
||||||
for task in asyncio.all_tasks():
|
for task in asyncio.all_tasks():
|
||||||
|
@ -822,71 +847,52 @@ class Kirdy:
|
||||||
await asyncio.gather(*asyncio.all_tasks(), loop=self._event_loop)
|
await asyncio.gather(*asyncio.all_tasks(), loop=self._event_loop)
|
||||||
|
|
||||||
async def __coninit(self, timeout):
|
async def __coninit(self, timeout):
|
||||||
self._task_queue = asyncio.Queue(maxsize=64)
|
def _put_nowait_overwrite(self, item):
|
||||||
|
if self.full():
|
||||||
|
self.get_nowait()
|
||||||
|
self.put_nowait(item)
|
||||||
|
asyncio.Queue.put_nowait_overwrite = _put_nowait_overwrite
|
||||||
|
|
||||||
|
if self._task_queue is not None:
|
||||||
|
while not(self._task_queue.empty()):
|
||||||
|
task = self._task_queue.get_nowait()
|
||||||
|
if isinstance(task, types.FunctionType):
|
||||||
|
task().close()
|
||||||
|
else:
|
||||||
|
self._task_queue = asyncio.Queue(maxsize=16)
|
||||||
self._int_msg_queue = asyncio.Queue(maxsize=4)
|
self._int_msg_queue = asyncio.Queue(maxsize=4)
|
||||||
self._msg_queue = queue.Queue(maxsize=64)
|
self._report_queue = asyncio.Queue(maxsize=16)
|
||||||
self._report_queue = queue.Queue(maxsize=16)
|
|
||||||
|
|
||||||
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(self._host, self._ctrl_port), timeout)
|
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(self._host, self._ctrl_port), timeout)
|
||||||
writer_sock = self._writer.get_extra_info("socket")
|
writer_sock = self._writer.get_extra_info("socket")
|
||||||
writer_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
writer_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
|
||||||
if self._connected_sig is not None:
|
async def _read_response(self):
|
||||||
self._connected_sig.emit(True)
|
|
||||||
|
|
||||||
async def _read_response(self, buffer_size=16384):
|
|
||||||
raw_response = b''
|
raw_response = b''
|
||||||
while len(raw_response) == 0:
|
while len(raw_response) == 0:
|
||||||
# Ignore 0 size packet
|
# Ignore 0 size packet
|
||||||
raw_response = await self._reader.read(buffer_size)
|
raw_response = await self._reader.readuntil()
|
||||||
response = raw_response.decode('utf-8', errors='ignore').split("\n")
|
response = raw_response.decode('utf-8', errors='ignore').split("\n")
|
||||||
response = response[:-1]
|
return json.loads(response[0])
|
||||||
|
|
||||||
items = []
|
async def _send_raw_cmd(self, cmd, msg_type="Acknowledge", sig=None):
|
||||||
for item in response:
|
|
||||||
items.append(json.loads(item))
|
|
||||||
return items
|
|
||||||
|
|
||||||
def _get_msg(self):
|
|
||||||
msg = self._msg_queue.get()
|
|
||||||
return msg
|
|
||||||
|
|
||||||
def _send_raw_cmd_handler(self, cmd, msg_type="Acknowledge", sig=None):
|
|
||||||
if self.connected():
|
|
||||||
self._event_loop.call_soon_threadsafe(self._task_queue.put_nowait, lambda: self._send_raw_cmd(cmd, msg_type, sig=sig))
|
|
||||||
else:
|
|
||||||
raise ConnectionError
|
|
||||||
|
|
||||||
# If the cmd involves a cmd specific data type,
|
|
||||||
# checking is done separately within the functions being called
|
|
||||||
async def _send_raw_cmd(self, cmd, msg_type, sig=None):
|
|
||||||
if self.connected():
|
if self.connected():
|
||||||
|
async with self._lock:
|
||||||
self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
|
self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
|
||||||
await self._writer.drain()
|
await self._writer.drain()
|
||||||
response = await self._int_msg_queue.get()
|
msg = await asyncio.wait_for(self._int_msg_queue.get(), self._timeout)
|
||||||
if response["msg_type"] == msg_type:
|
|
||||||
|
if msg["msg_type"] == msg_type:
|
||||||
if sig is not None:
|
if sig is not None:
|
||||||
sig.emit(response)
|
sig.emit(msg)
|
||||||
return {"msg_type": "Acknowledge"}
|
return {"msg_type": "Acknowledge"}
|
||||||
return response
|
return msg
|
||||||
else:
|
else:
|
||||||
raise InvalidCmd
|
raise InvalidCmd
|
||||||
else:
|
else:
|
||||||
raise ConnectionError
|
raise ConnectionError
|
||||||
|
|
||||||
def _send_cmd_handler(self, target, cmd, data=None, msg_type="Acknowledge", sig=None, hard_reset=False):
|
async def _send_cmd(self, target, cmd, data=None, msg_type="Acknowledge", sig=None):
|
||||||
if self.connected():
|
|
||||||
if hard_reset:
|
|
||||||
while not(self._task_queue.empty()):
|
|
||||||
pass
|
|
||||||
self._event_loop.call_soon_threadsafe(self._task_queue.put_nowait, lambda: self._send_cmd(target, cmd, data, msg_type, sig=sig))
|
|
||||||
if hard_reset:
|
|
||||||
# Wait 1s for Kirdy to hard reset
|
|
||||||
time.sleep(1.0)
|
|
||||||
else:
|
|
||||||
raise ConnectionError
|
|
||||||
|
|
||||||
async def _send_cmd(self, target, cmd, data, msg_type, sig=None):
|
|
||||||
cmd_dict = {}
|
cmd_dict = {}
|
||||||
cmd_dict[target] = cmd.name
|
cmd_dict[target] = cmd.name
|
||||||
|
|
||||||
|
@ -904,17 +910,21 @@ class Kirdy:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if msg_type == 'Report':
|
if msg_type == 'Report':
|
||||||
async with self._lock:
|
|
||||||
self._msg_queue_get_report = True
|
self._msg_queue_get_report = True
|
||||||
|
|
||||||
|
async with self._lock:
|
||||||
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
|
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
|
||||||
await self._writer.drain()
|
await self._writer.drain()
|
||||||
|
|
||||||
msg = await self._int_msg_queue.get()
|
msg = await asyncio.wait_for(self._int_msg_queue.get(), self._timeout)
|
||||||
|
if isinstance(msg, Exception):
|
||||||
|
raise msg
|
||||||
|
|
||||||
if msg['msg_type'] == msg_type:
|
if msg['msg_type'] == msg_type:
|
||||||
if sig is not None:
|
if sig is not None:
|
||||||
sig.emit(msg)
|
sig.emit(msg)
|
||||||
return {"msg_type": "Acknowledge"}
|
return {"msg_type": "Acknowledge"}
|
||||||
|
else:
|
||||||
return msg
|
return msg
|
||||||
else:
|
else:
|
||||||
raise InvalidCmd
|
raise InvalidCmd
|
||||||
|
|
|
@ -20,7 +20,7 @@ import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from driver.kirdy import Kirdy as Kirdy_Driver
|
from driver.kirdy import Kirdy as Kirdy_Driver
|
||||||
import qasync
|
import qasync
|
||||||
from qasync import asyncClose
|
from qasync import asyncClose, asyncSlot
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from time import time
|
from time import time
|
||||||
|
@ -96,15 +96,16 @@ class Kirdy(QObject):
|
||||||
def end_session(self):
|
def end_session(self):
|
||||||
if self._timer.isActive():
|
if self._timer.isActive():
|
||||||
self._timer.stop()
|
self._timer.stop()
|
||||||
self._kirdy.end_session()
|
asyncio.get_running_loop().create_task(self._kirdy.end_session())
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def connected_setup(self, connected):
|
def connected_setup(self, connected):
|
||||||
if connected:
|
if connected:
|
||||||
self._kirdy.device.set_active_report_mode(True)
|
self._kirdy.task_dispatcher(self._kirdy.device.set_active_report_mode(True))
|
||||||
|
self._kirdy._report_mode_on = True
|
||||||
|
|
||||||
def timerEvent(self, event):
|
def timerEvent(self, event):
|
||||||
self._kirdy.device.get_settings_summary(sig=self.setting_update_sig)
|
self._kirdy.task_dispatcher(self._kirdy.device.get_settings_summary(sig=self.setting_update_sig))
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def start_polling(self, start):
|
def start_polling(self, start):
|
||||||
|
@ -128,6 +129,9 @@ class Kirdy(QObject):
|
||||||
else:
|
else:
|
||||||
logging.debug("Attempt to update polling timer when it is stopped")
|
logging.debug("Attempt to update polling timer when it is stopped")
|
||||||
|
|
||||||
|
def get_hw_rev(self):
|
||||||
|
return self._kirdy.get_hw_rev()
|
||||||
|
|
||||||
class Graphs:
|
class Graphs:
|
||||||
def __init__(self, ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph, max_samples=1000):
|
def __init__(self, ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph, max_samples=1000):
|
||||||
self.graphs = [ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph]
|
self.graphs = [ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph]
|
||||||
|
@ -524,12 +528,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
def setup_menu_bar(self):
|
def setup_menu_bar(self):
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def about_kirdy(_):
|
def about_kirdy(_):
|
||||||
# TODO: Replace the hardware revision placeholder
|
hw_rev = self.kirdy_handler.get_hw_rev()
|
||||||
QtWidgets.QMessageBox.about(
|
QtWidgets.QMessageBox.about(
|
||||||
self,
|
self,
|
||||||
"About Kirdy",
|
"About Kirdy",
|
||||||
f"""
|
f"""
|
||||||
<h1>Sinara 1550 Kirdy v"major rev"."minor rev"</h1>
|
<h1>Sinara 1550 Kirdy v{hw_rev["major"]}.{hw_rev["minor"]}</h1>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
self.menu_action_about_kirdy.triggered.connect(about_kirdy)
|
self.menu_action_about_kirdy.triggered.connect(about_kirdy)
|
||||||
|
@ -548,19 +552,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def dfu_mode(_):
|
def dfu_mode(_):
|
||||||
self.kirdy.device.dfu()
|
self.kirdy.task_dispatcher(self.kirdy.device.dfu())
|
||||||
self.kirdy_handler.end_session()
|
self.kirdy_handler.end_session()
|
||||||
self.menu_action_dfu_mode.triggered.connect(dfu_mode)
|
self.menu_action_dfu_mode.triggered.connect(dfu_mode)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def reset_kirdy(_):
|
def reset_kirdy(_):
|
||||||
self.kirdy.device.hard_reset()
|
self.kirdy.task_dispatcher(self.kirdy.device.hard_reset())
|
||||||
self.kirdy_handler.end_session()
|
self.kirdy_handler.end_session()
|
||||||
self.menu_action_hard_reset.triggered.connect(reset_kirdy)
|
self.menu_action_hard_reset.triggered.connect(reset_kirdy)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def save_settings(_):
|
def save_settings(_):
|
||||||
self.kirdy.device.save_current_settings_to_flash()
|
self.kirdy.task_dispatcher(self.kirdy.device.save_current_settings_to_flash())
|
||||||
saved = QtWidgets.QMessageBox(self)
|
saved = QtWidgets.QMessageBox(self)
|
||||||
saved.setWindowTitle("Config saved")
|
saved.setWindowTitle("Config saved")
|
||||||
saved.setText(f"Laser diode and thermostat configs have been saved into flash.")
|
saved.setText(f"Laser diode and thermostat configs have been saved into flash.")
|
||||||
|
@ -570,7 +574,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def load_settings(_):
|
def load_settings(_):
|
||||||
self.kirdy.device.restore_settings_from_flash()
|
self.kirdy.task_dispatcher(self.kirdy.device.restore_settings_from_flash())
|
||||||
loaded = QtWidgets.QMessageBox(self)
|
loaded = QtWidgets.QMessageBox(self)
|
||||||
loaded.setWindowTitle("Config loaded")
|
loaded.setWindowTitle("Config loaded")
|
||||||
loaded.setText(f"Laser Diode and Thermostat configs have been loaded from flash.")
|
loaded.setText(f"Laser Diode and Thermostat configs have been loaded from flash.")
|
||||||
|
@ -596,32 +600,32 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
def _set_up_ctrl_btns(self):
|
def _set_up_ctrl_btns(self):
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def ld_pwr_on(_):
|
def ld_pwr_on(_):
|
||||||
self.kirdy.laser.set_power_on(True)
|
self.kirdy.task_dispatcher(self.kirdy.laser.set_power_on(True))
|
||||||
self.ld_pwr_on_btn.clicked.connect(ld_pwr_on)
|
self.ld_pwr_on_btn.clicked.connect(ld_pwr_on)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def ld_pwr_off(_):
|
def ld_pwr_off(_):
|
||||||
self.kirdy.laser.set_power_on(False)
|
self.kirdy.task_dispatcher(self.kirdy.laser.set_power_on(False))
|
||||||
self.ld_pwr_off_btn.clicked.connect(ld_pwr_off)
|
self.ld_pwr_off_btn.clicked.connect(ld_pwr_off)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def ld_clear_alarm(_):
|
def ld_clear_alarm(_):
|
||||||
self.kirdy.laser.clear_alarm()
|
self.kirdy.task_dispatcher(self.kirdy.laser.clear_alarm())
|
||||||
self.ld_clear_alarm_btn.clicked.connect(ld_clear_alarm)
|
self.ld_clear_alarm_btn.clicked.connect(ld_clear_alarm)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def tec_pwr_on(_):
|
def tec_pwr_on(_):
|
||||||
self.kirdy.thermostat.set_power_on(True)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_power_on(True))
|
||||||
self.tec_pwr_on_btn.clicked.connect(tec_pwr_on)
|
self.tec_pwr_on_btn.clicked.connect(tec_pwr_on)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def tec_pwr_off(_):
|
def tec_pwr_off(_):
|
||||||
self.kirdy.thermostat.set_power_on(False)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_power_on(False))
|
||||||
self.tec_pwr_off_btn.clicked.connect(tec_pwr_off)
|
self.tec_pwr_off_btn.clicked.connect(tec_pwr_off)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def tec_clear_alarm(_):
|
def tec_clear_alarm(_):
|
||||||
self.kirdy.thermostat.clear_alarm()
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.clear_alarm())
|
||||||
self.tec_clear_alarm_btn.clicked.connect(tec_clear_alarm)
|
self.tec_clear_alarm_btn.clicked.connect(tec_clear_alarm)
|
||||||
|
|
||||||
def _set_up_plot_menu(self):
|
def _set_up_plot_menu(self):
|
||||||
|
@ -665,11 +669,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
tree.setParameters(self.params[3], showTop=False)
|
tree.setParameters(self.params[3], showTop=False)
|
||||||
self.params[3].sigTreeStateChanged.connect(self.send_command)
|
self.params[3].sigTreeStateChanged.connect(self.send_command)
|
||||||
|
|
||||||
@pyqtSlot()
|
@asyncSlot()
|
||||||
def autotune(param):
|
async def autotune(param):
|
||||||
match self.autotuner.state():
|
match self.autotuner.state():
|
||||||
case PIDAutotuneState.STATE_OFF:
|
case PIDAutotuneState.STATE_OFF:
|
||||||
settings = self.kirdy.device.get_settings_summary()
|
settings = await self.kirdy.device.get_settings_summary()
|
||||||
self.autotuner.setParam(
|
self.autotuner.setParam(
|
||||||
param.parent().child('Target Temperature').value(),
|
param.parent().child('Target Temperature').value(),
|
||||||
param.parent().child('Test Current').value() / 1000,
|
param.parent().child('Test Current').value() / 1000,
|
||||||
|
@ -678,7 +682,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
param.parent().child('Lookback').value())
|
param.parent().child('Lookback').value())
|
||||||
self.autotuner.setReady()
|
self.autotuner.setReady()
|
||||||
param.setOpts(title="Stop")
|
param.setOpts(title="Stop")
|
||||||
self.kirdy.thermostat.set_constant_current_control_mode()
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_constant_current_control_mode())
|
||||||
self.kirdy_handler.report_update_sig.connect(self.autotune_tick)
|
self.kirdy_handler.report_update_sig.connect(self.autotune_tick)
|
||||||
self.loading_spinner.show()
|
self.loading_spinner.show()
|
||||||
self.loading_spinner.start()
|
self.loading_spinner.start()
|
||||||
|
@ -686,7 +690,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
||||||
self.autotuner.setOff()
|
self.autotuner.setOff()
|
||||||
param.setOpts(title="Run")
|
param.setOpts(title="Run")
|
||||||
self.kirdy.thermostat.set_tec_i_out(0.0)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0))
|
||||||
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
||||||
self.background_task_lbl.setText("Ready.")
|
self.background_task_lbl.setText("Ready.")
|
||||||
self.loading_spinner.stop()
|
self.loading_spinner.stop()
|
||||||
|
@ -698,16 +702,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
match self.autotuner.state():
|
match self.autotuner.state():
|
||||||
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
||||||
self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000)
|
self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000)
|
||||||
self.kirdy.thermostat.set_tec_i_out(self.autotuner.output())
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(self.autotuner.output()))
|
||||||
case PIDAutotuneState.STATE_SUCCEEDED:
|
case PIDAutotuneState.STATE_SUCCEEDED:
|
||||||
kp, ki, kd = self.autotuner.get_tec_pid()
|
kp, ki, kd = self.autotuner.get_tec_pid()
|
||||||
self.autotuner.setOff()
|
self.autotuner.setOff()
|
||||||
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
|
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
|
||||||
self.kirdy.thermostat.set_pid_kp(kp)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kp(kp))
|
||||||
self.kirdy.thermostat.set_pid_ki(ki)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki))
|
||||||
self.kirdy.thermostat.set_pid_kd(kd)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd))
|
||||||
self.kirdy.thermostat.set_pid_control_mode()
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode())
|
||||||
self.kirdy.thermostat.set_temperature_setpoint(self.params[3].child('PID Config', 'PID Auto Tune', 'Target Temperature').value())
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_temperature_setpoint(self.params[3].child('PID Config', 'PID Auto Tune', 'Target Temperature').value()))
|
||||||
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
||||||
self.background_task_lbl.setText("Ready.")
|
self.background_task_lbl.setText("Ready.")
|
||||||
self.loading_spinner.stop()
|
self.loading_spinner.stop()
|
||||||
|
@ -719,7 +723,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
case PIDAutotuneState.STATE_FAILED:
|
case PIDAutotuneState.STATE_FAILED:
|
||||||
self.autotuner.setOff()
|
self.autotuner.setOff()
|
||||||
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
|
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
|
||||||
self.kirdy.thermostat.set_tec_i_out(0.0)
|
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0))
|
||||||
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
|
||||||
self.background_task_lbl.setText("Ready.")
|
self.background_task_lbl.setText("Ready.")
|
||||||
self.loading_spinner.stop()
|
self.loading_spinner.stop()
|
||||||
|
@ -768,7 +772,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
if result:
|
if result:
|
||||||
self.connect_btn.setText("Disconnect")
|
self.connect_btn.setText("Disconnect")
|
||||||
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
||||||
# TODO: self.hw_rev_data = self.kirdy.hw_rev()
|
|
||||||
self._status()
|
self._status()
|
||||||
else:
|
else:
|
||||||
if self.kirdy_handler.connecting():
|
if self.kirdy_handler.connecting():
|
||||||
|
@ -782,10 +785,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
||||||
|
|
||||||
def _status(self):
|
def _status(self):
|
||||||
# TODO: Get rev no from Kirdy and then add revision into the text
|
|
||||||
host = self.ip_addr
|
host = self.ip_addr
|
||||||
port = self.port
|
port = self.port
|
||||||
self.status_lbl.setText(f"Connected to {host}:{port}")
|
hw_rev = self.kirdy_handler.get_hw_rev()
|
||||||
|
self.status_lbl.setText(f"Connected to Kirdy v{hw_rev['major']}.{hw_rev['minor']} @ {host}:{port}")
|
||||||
|
|
||||||
def clear_graphs(self):
|
def clear_graphs(self):
|
||||||
self.graphs.clear_data_pts()
|
self.graphs.clear_data_pts()
|
||||||
|
@ -899,7 +902,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
port = net_settings["port"]
|
port = net_settings["port"]
|
||||||
prefix_len = net_settings["prefix_len"]
|
prefix_len = net_settings["prefix_len"]
|
||||||
gateway = net_settings["gateway_addr"]
|
gateway = net_settings["gateway_addr"]
|
||||||
self.kirdy.device.set_ip_settings(addr, port, prefix_len, gateway)
|
self.kirdy.task_dispatcher(self.kirdy.device.set_ip_settings(addr, port, prefix_len, gateway))
|
||||||
self.status_lbl.setText("IP Settings is Updated")
|
self.status_lbl.setText("IP Settings is Updated")
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
|
@ -931,7 +934,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
target, action = inner_param.opts['target_action_pair'][inner_param.opts['limits'].index(data)]
|
target, action = inner_param.opts['target_action_pair'][inner_param.opts['limits'].index(data)]
|
||||||
cmd = getattr(getattr(self.kirdy, target), action)
|
cmd = getattr(getattr(self.kirdy, target), action)
|
||||||
param.child(*param.childPath(inner_param)).setOpts(lock=True)
|
param.child(*param.childPath(inner_param)).setOpts(lock=True)
|
||||||
cmd()
|
self.kirdy.task_dispatcher(cmd())
|
||||||
param.child(*param.childPath(inner_param)).setOpts(lock=False)
|
param.child(*param.childPath(inner_param)).setOpts(lock=False)
|
||||||
continue
|
continue
|
||||||
""" cmd translation from non-mutex type parameter"""
|
""" cmd translation from non-mutex type parameter"""
|
||||||
|
@ -942,25 +945,27 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
data = siEval(str(data)+inner_param.opts["unit"], regex=FLOAT_REGEX, suffix=suffix)
|
data = siEval(str(data)+inner_param.opts["unit"], regex=FLOAT_REGEX, suffix=suffix)
|
||||||
cmd = getattr(getattr(self.kirdy, inner_param.opts["target"]), inner_param.opts["action"])
|
cmd = getattr(getattr(self.kirdy, inner_param.opts["target"]), inner_param.opts["action"])
|
||||||
param.child(*param.childPath(inner_param)).setOpts(lock=True)
|
param.child(*param.childPath(inner_param)).setOpts(lock=True)
|
||||||
cmd(data)
|
self.kirdy.task_dispatcher(cmd(data))
|
||||||
param.child(*param.childPath(inner_param)).setOpts(lock=False)
|
param.child(*param.childPath(inner_param)).setOpts(lock=False)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def coro_main():
|
async def coro_main():
|
||||||
args = get_argparser().parse_args()
|
args = get_argparser().parse_args()
|
||||||
if args.logLevel:
|
if args.logLevel:
|
||||||
logging.basicConfig(level=getattr(logging, args.logLevel))
|
logging.basicConfig(level=getattr(logging, args.logLevel))
|
||||||
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app_quit_event = asyncio.Event()
|
||||||
|
|
||||||
|
app = QtWidgets.QApplication.instance()
|
||||||
|
app.aboutToQuit.connect(app_quit_event.set)
|
||||||
|
|
||||||
main_window = MainWindow(args)
|
main_window = MainWindow(args)
|
||||||
main_window.show()
|
main_window.show()
|
||||||
|
|
||||||
app.aboutToQuit.connect(main_window.kirdy_handler.end_session)
|
await app_quit_event.wait()
|
||||||
app.exec()
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
coro_main()
|
qasync.run(coro_main())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import time
|
||||||
import signal
|
import signal
|
||||||
from driver.kirdy import Kirdy, FilterConfig
|
from driver.kirdy import Kirdy, FilterConfig
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from sipyco.asyncio_tools import SignalHandler
|
||||||
|
|
||||||
# Based on hirshmann pid-autotune libiary
|
# Based on hirshmann pid-autotune libiary
|
||||||
# See https://github.com/hirschmann/pid-autotune
|
# See https://github.com/hirschmann/pid-autotune
|
||||||
|
@ -76,6 +77,10 @@ class PIDAutotune:
|
||||||
def setOff(self):
|
def setOff(self):
|
||||||
self._state = PIDAutotuneState.STATE_OFF
|
self._state = PIDAutotuneState.STATE_OFF
|
||||||
|
|
||||||
|
def setFailed(self):
|
||||||
|
self._state = PIDAutotuneState.STATE_FAILED
|
||||||
|
self._peak_count = 30
|
||||||
|
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Get the current state."""
|
"""Get the current state."""
|
||||||
return self._state
|
return self._state
|
||||||
|
@ -246,7 +251,7 @@ class PIDAutotune:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
async def main():
|
||||||
"""
|
"""
|
||||||
PID AutoTune Tools for Kirdy
|
PID AutoTune Tools for Kirdy
|
||||||
The obtained temperature works best at the target temperature specified.
|
The obtained temperature works best at the target temperature specified.
|
||||||
|
@ -271,46 +276,46 @@ def main():
|
||||||
noiseband = 2.0
|
noiseband = 2.0
|
||||||
|
|
||||||
kirdy = Kirdy()
|
kirdy = Kirdy()
|
||||||
kirdy.start_session(host='192.168.1.128', port=1337)
|
kirdy.start_session(host='192.168.1.126', port=1337)
|
||||||
|
|
||||||
|
await kirdy.wait_until_connected()
|
||||||
|
|
||||||
while not(kirdy.connected()):
|
while not(kirdy.connected()):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kirdy.laser.set_power_on(False)
|
await kirdy.laser.set_power_on(False)
|
||||||
kirdy.laser.set_i(0)
|
await kirdy.laser.set_i(0)
|
||||||
|
|
||||||
kirdy.thermostat.set_power_on(False)
|
await kirdy.thermostat.set_power_on(False)
|
||||||
kirdy.thermostat.set_constant_current_control_mode()
|
await kirdy.thermostat.set_constant_current_control_mode()
|
||||||
kirdy.thermostat.set_tec_i_out(0)
|
await kirdy.thermostat.set_tec_i_out(0)
|
||||||
kirdy.thermostat.clear_alarm()
|
await kirdy.thermostat.clear_alarm()
|
||||||
|
|
||||||
class SignalHandler:
|
|
||||||
KEEP_PROCESSING = True
|
|
||||||
def __init__(self):
|
|
||||||
signal.signal(signal.SIGINT, self.exit_gracefully)
|
|
||||||
signal.signal(signal.SIGTERM, self.exit_gracefully)
|
|
||||||
|
|
||||||
def exit_gracefully(self, signum, frame):
|
|
||||||
self.KEEP_PROCESSING = False
|
|
||||||
signal_handler = SignalHandler()
|
signal_handler = SignalHandler()
|
||||||
|
signal_handler.setup()
|
||||||
|
async def sig_handling():
|
||||||
|
await signal_handler.wait_terminate()
|
||||||
|
tuner.setFailed()
|
||||||
|
asyncio.create_task(sig_handling())
|
||||||
|
|
||||||
kirdy.device.set_active_report_mode(False)
|
await kirdy.device.set_active_report_mode(False)
|
||||||
|
|
||||||
# Configure the Thermistor Parameters
|
# Configure the Thermistor Parameters
|
||||||
kirdy.thermostat.set_sh_beta(3950)
|
await kirdy.thermostat.set_sh_beta(3950)
|
||||||
kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||||
kirdy.thermostat.set_sh_t0(25)
|
await kirdy.thermostat.set_sh_t0(25)
|
||||||
|
|
||||||
# Set a large enough temperature range so that it won't trigger overtemperature protection
|
# Set a large enough temperature range so that it won't trigger overtemperature protection
|
||||||
kirdy.thermostat.set_temp_mon_upper_limit(target_temperature + 20)
|
await kirdy.thermostat.set_temp_mon_upper_limit(target_temperature + 20)
|
||||||
kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20)
|
await kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20)
|
||||||
|
|
||||||
kirdy.thermostat.set_tec_max_cooling_i(output_step)
|
await kirdy.thermostat.set_tec_max_cooling_i(output_step)
|
||||||
kirdy.thermostat.set_tec_max_heating_i(output_step)
|
await kirdy.thermostat.set_tec_max_heating_i(output_step)
|
||||||
|
|
||||||
# The Polling Rate of Temperature Adc is equal to the PID Update Interval
|
# The Polling Rate of Temperature Adc is equal to the PID Update Interval
|
||||||
kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||||
settings = kirdy.device.get_settings_summary()
|
settings = await kirdy.device.get_settings_summary()
|
||||||
sampling_rate = settings["thermostat"]["temp_adc_settings"]["rate"]
|
sampling_rate = settings["thermostat"]["temp_adc_settings"]["rate"]
|
||||||
|
|
||||||
print("Settings: {0}".format(settings))
|
print("Settings: {0}".format(settings))
|
||||||
|
@ -318,10 +323,10 @@ def main():
|
||||||
tuner = PIDAutotune(target_temperature, output_step,
|
tuner = PIDAutotune(target_temperature, output_step,
|
||||||
lookback, noiseband, 1/sampling_rate)
|
lookback, noiseband, 1/sampling_rate)
|
||||||
|
|
||||||
kirdy.thermostat.set_power_on(True)
|
await kirdy.thermostat.set_power_on(True)
|
||||||
|
|
||||||
while True and signal_handler.KEEP_PROCESSING:
|
while True:
|
||||||
status_report = kirdy.device.get_status_report()
|
status_report = await kirdy.device.get_status_report()
|
||||||
|
|
||||||
temperature = status_report["thermostat"]["temperature"]
|
temperature = status_report["thermostat"]["temperature"]
|
||||||
ts = status_report['ts']
|
ts = status_report['ts']
|
||||||
|
@ -332,19 +337,20 @@ def main():
|
||||||
break
|
break
|
||||||
|
|
||||||
tuner_out = tuner.output()
|
tuner_out = tuner.output()
|
||||||
kirdy.thermostat.set_tec_i_out(float(tuner_out))
|
await kirdy.thermostat.set_tec_i_out(float(tuner_out))
|
||||||
|
|
||||||
kirdy.thermostat.set_tec_i_out(0)
|
await kirdy.thermostat.set_tec_i_out(0)
|
||||||
kirdy.thermostat.set_power_on(False)
|
await kirdy.thermostat.set_power_on(False)
|
||||||
|
|
||||||
|
if tuner.state() == PIDAutotuneState.STATE_SUCCEEDED:
|
||||||
pid_params = tuner.get_pid_parameters(tuning_rule="tyreus-luyben")
|
pid_params = tuner.get_pid_parameters(tuning_rule="tyreus-luyben")
|
||||||
kirdy.thermostat.set_pid_kp(pid_params.Kp)
|
await kirdy.thermostat.set_pid_kp(pid_params.Kp)
|
||||||
kirdy.thermostat.set_pid_ki(pid_params.Ki)
|
await kirdy.thermostat.set_pid_ki(pid_params.Ki)
|
||||||
kirdy.thermostat.set_pid_kd(pid_params.Kd)
|
await kirdy.thermostat.set_pid_kd(pid_params.Kd)
|
||||||
kirdy.thermostat.set_pid_output_max(1.0)
|
await kirdy.thermostat.set_pid_output_max(1.0)
|
||||||
kirdy.thermostat.set_pid_output_min(1.0)
|
await kirdy.thermostat.set_pid_output_min(1.0)
|
||||||
|
|
||||||
kirdy.end_session(block=True)
|
await kirdy.end_session()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
asyncio.run(main())
|
||||||
|
|
|
@ -9,7 +9,8 @@ use uom::si::{electric_current::{ampere, milliampere},
|
||||||
f32::ElectricCurrent};
|
f32::ElectricCurrent};
|
||||||
|
|
||||||
use super::{gpio, sys_timer, usb};
|
use super::{gpio, sys_timer, usb};
|
||||||
use crate::{device::flash_store::{self, FlashStore},
|
use crate::{device::{flash_store::{self, FlashStore},
|
||||||
|
hw_rev::HWRev},
|
||||||
laser_diode::{laser_diode::LdDrive, ld_ctrl::*},
|
laser_diode::{laser_diode::LdDrive, ld_ctrl::*},
|
||||||
net::net::{IpSettings, ServerHandle},
|
net::net::{IpSettings, ServerHandle},
|
||||||
thermostat::{max1968::MAX1968, thermostat::Thermostat},
|
thermostat::{max1968::MAX1968, thermostat::Thermostat},
|
||||||
|
@ -23,7 +24,7 @@ const WATCHDOG_PERIOD: u32 = 30000;
|
||||||
pub fn bootup(
|
pub fn bootup(
|
||||||
mut core_perif: CorePeripherals,
|
mut core_perif: CorePeripherals,
|
||||||
perif: Peripherals,
|
perif: Peripherals,
|
||||||
) -> (IndependentWatchdog, FlashStore, LdDrive, Thermostat) {
|
) -> (IndependentWatchdog, FlashStore, HWRev, LdDrive, Thermostat) {
|
||||||
core_perif.SCB.enable_icache();
|
core_perif.SCB.enable_icache();
|
||||||
core_perif.SCB.enable_dcache(&mut core_perif.CPUID);
|
core_perif.SCB.enable_dcache(&mut core_perif.CPUID);
|
||||||
|
|
||||||
|
@ -119,5 +120,5 @@ pub fn bootup(
|
||||||
|
|
||||||
info!("Kirdy setup complete");
|
info!("Kirdy setup complete");
|
||||||
|
|
||||||
(wd, flash_store, laser, thermostat)
|
(wd, flash_store, hw_rev, laser, thermostat)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crc::{Crc, CRC_24_BLE};
|
use crc::{Crc, CRC_24_BLE};
|
||||||
|
use miniconf::Tree;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use stm32f4xx_hal::{gpio::{Input, PE10, PE11, PE8, PE9},
|
use stm32f4xx_hal::{gpio::{Input, PE10, PE11, PE8, PE9},
|
||||||
signature};
|
signature};
|
||||||
|
|
||||||
|
@ -11,6 +13,7 @@ pub struct HwRevPins {
|
||||||
pub h3: PE11<Input>,
|
pub h3: PE11<Input>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||||
pub struct HWRev {
|
pub struct HWRev {
|
||||||
pub major: u8,
|
pub major: u8,
|
||||||
pub minor: u8,
|
pub minor: u8,
|
||||||
|
|
62
src/main.rs
62
src/main.rs
|
@ -1,7 +1,7 @@
|
||||||
#![cfg_attr(not(test), no_main)]
|
#![cfg_attr(not(test), no_main)]
|
||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::{marker::PhantomData, u32};
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
|
@ -52,6 +52,7 @@ pub enum State {
|
||||||
SaveLdThermostatSettings,
|
SaveLdThermostatSettings,
|
||||||
SaveDeviceSettings,
|
SaveDeviceSettings,
|
||||||
PrepareForHardReset,
|
PrepareForHardReset,
|
||||||
|
PrepareForDfu,
|
||||||
HardReset,
|
HardReset,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ fn main() -> ! {
|
||||||
let core_perif = CorePeripherals::take().unwrap();
|
let core_perif = CorePeripherals::take().unwrap();
|
||||||
let perif = Peripherals::take().unwrap();
|
let perif = Peripherals::take().unwrap();
|
||||||
|
|
||||||
let (mut wd, mut flash_store, mut laser, mut thermostat) = bootup(core_perif, perif);
|
let (mut wd, mut flash_store, mut hw_rev, mut laser, mut thermostat) = bootup(core_perif, perif);
|
||||||
|
|
||||||
let mut device_settings = DeviceSettings {
|
let mut device_settings = DeviceSettings {
|
||||||
ip_settings: IpSettings::default(),
|
ip_settings: IpSettings::default(),
|
||||||
|
@ -82,6 +83,8 @@ fn main() -> ! {
|
||||||
|
|
||||||
let eth_data_buffer = unsafe { addr_of_mut!(ETH_DATA_BUFFER).as_mut().unwrap() };
|
let eth_data_buffer = unsafe { addr_of_mut!(ETH_DATA_BUFFER).as_mut().unwrap() };
|
||||||
|
|
||||||
|
let mut sock_ts: [u32; net::net::NUM_OF_SOCKETS] = [0; net::net::NUM_OF_SOCKETS];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
wd.feed();
|
wd.feed();
|
||||||
|
|
||||||
|
@ -183,6 +186,10 @@ fn main() -> ! {
|
||||||
thermostat.start_tec_readings_conversion();
|
thermostat.start_tec_readings_conversion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut num_of_connected_sock: u8 = 0;
|
||||||
|
let mut oldest_connected_sock_ts: u32 = u32::MAX;
|
||||||
|
let mut oldest_connected_sock_id: usize = 0;
|
||||||
|
|
||||||
net::net::for_each(|mut socket, id| {
|
net::net::for_each(|mut socket, id| {
|
||||||
if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) {
|
if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) {
|
||||||
if net::net::eth_can_sock_recv(socket) && net::net::eth_can_sock_send(socket) {
|
if net::net::eth_can_sock_recv(socket) && net::net::eth_can_sock_send(socket) {
|
||||||
|
@ -195,6 +202,7 @@ fn main() -> ! {
|
||||||
eth_data_buffer,
|
eth_data_buffer,
|
||||||
bytes,
|
bytes,
|
||||||
&mut socket,
|
&mut socket,
|
||||||
|
&mut hw_rev,
|
||||||
&mut laser,
|
&mut laser,
|
||||||
&mut thermostat,
|
&mut thermostat,
|
||||||
&mut state,
|
&mut state,
|
||||||
|
@ -203,8 +211,35 @@ fn main() -> ! {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
num_of_connected_sock += 1;
|
||||||
|
|
||||||
|
if sock_ts[id] == 0 {
|
||||||
|
sock_ts[id] = sys_timer::now();
|
||||||
|
}
|
||||||
|
if oldest_connected_sock_ts > sock_ts[id] {
|
||||||
|
oldest_connected_sock_ts = sock_ts[id];
|
||||||
|
oldest_connected_sock_id = id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sock_ts[id] = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if num_of_connected_sock == net::net::NUM_OF_SOCKETS as u8 {
|
||||||
|
let mut sock_handle = net::net::eth_get_sock_handle(oldest_connected_sock_id);
|
||||||
|
net::cmd_handler::send_response(
|
||||||
|
eth_data_buffer,
|
||||||
|
net::cmd_handler::ResponseEnum::ConnectionClose,
|
||||||
|
None,
|
||||||
|
&mut sock_handle,
|
||||||
|
);
|
||||||
|
net::net::eth_poll_iface();
|
||||||
|
debug!("Waiting for ConnectionClose msg to be sent");
|
||||||
|
while !net::net::eth_is_data_sent() { }
|
||||||
|
net::net::eth_close_socket_by_id(oldest_connected_sock_id);
|
||||||
|
debug!("Closing socket id: {:?}", oldest_connected_sock_id);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
State::SaveLdThermostatSettings => {
|
State::SaveLdThermostatSettings => {
|
||||||
// State Transition
|
// State Transition
|
||||||
|
@ -261,9 +296,30 @@ fn main() -> ! {
|
||||||
None,
|
None,
|
||||||
&mut socket,
|
&mut socket,
|
||||||
);
|
);
|
||||||
|
net::net::eth_poll_iface();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
State::PrepareForDfu => {
|
||||||
|
// State Transition
|
||||||
|
state = State::HardReset;
|
||||||
|
|
||||||
|
wd.feed();
|
||||||
|
laser.power_down();
|
||||||
|
thermostat.power_down();
|
||||||
|
net::net::for_each(|mut socket, _| {
|
||||||
|
if net::net::eth_is_socket_active(socket) {
|
||||||
|
net::cmd_handler::send_response(
|
||||||
|
eth_data_buffer,
|
||||||
|
net::cmd_handler::ResponseEnum::Dfu,
|
||||||
|
None,
|
||||||
|
&mut socket,
|
||||||
|
);
|
||||||
|
net::net::eth_poll_iface();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
State::HardReset => {
|
State::HardReset => {
|
||||||
wd.feed();
|
wd.feed();
|
||||||
laser.power_down();
|
laser.power_down();
|
||||||
|
|
|
@ -10,7 +10,7 @@ use uom::si::{electric_current::{ampere, ElectricCurrent},
|
||||||
electrical_resistance::{ohm, ElectricalResistance},
|
electrical_resistance::{ohm, ElectricalResistance},
|
||||||
power::{watt, Power}};
|
power::{watt, Power}};
|
||||||
|
|
||||||
use crate::{device::{dfu, sys_timer},
|
use crate::{device::{dfu, hw_rev::HWRev, sys_timer},
|
||||||
laser_diode::{laser_diode::{LdDrive, LdSettingsSummary, StatusReport as LdStatusReport},
|
laser_diode::{laser_diode::{LdDrive, LdSettingsSummary, StatusReport as LdStatusReport},
|
||||||
pd_mon_params::{self, ResponsitivityUnit}},
|
pd_mon_params::{self, ResponsitivityUnit}},
|
||||||
net::net,
|
net::net,
|
||||||
|
@ -26,10 +26,13 @@ pub enum ResponseEnum {
|
||||||
Reserved,
|
Reserved,
|
||||||
Settings,
|
Settings,
|
||||||
Report,
|
Report,
|
||||||
|
HwRev,
|
||||||
Acknowledge,
|
Acknowledge,
|
||||||
InvalidDatatype,
|
InvalidDatatype,
|
||||||
InvalidCmd,
|
InvalidCmd,
|
||||||
HardReset,
|
HardReset,
|
||||||
|
Dfu,
|
||||||
|
ConnectionClose,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MsgType = Option<&'static str>;
|
pub type MsgType = Option<&'static str>;
|
||||||
|
@ -59,6 +62,7 @@ pub struct ResponseObj<'a> {
|
||||||
enum DeviceCmd {
|
enum DeviceCmd {
|
||||||
#[default]
|
#[default]
|
||||||
Reserved,
|
Reserved,
|
||||||
|
GetHwRev,
|
||||||
SetIPSettings,
|
SetIPSettings,
|
||||||
SetActiveReportMode,
|
SetActiveReportMode,
|
||||||
SetPdFinGain,
|
SetPdFinGain,
|
||||||
|
@ -173,6 +177,17 @@ pub struct SettingsSummaryObj {
|
||||||
json: SettingsSummary,
|
json: SettingsSummary,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||||
|
pub struct HwRevType {
|
||||||
|
msg_type: ResponseEnum,
|
||||||
|
hw_rev: HWRev,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||||
|
pub struct HwRevObj {
|
||||||
|
json: HwRevType,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, socket: &mut SocketHandle) {
|
pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, socket: &mut SocketHandle) {
|
||||||
let response = ResponseObj {
|
let response = ResponseObj {
|
||||||
json: Response {
|
json: Response {
|
||||||
|
@ -227,6 +242,18 @@ pub fn send_status_report(
|
||||||
net::eth_send(buffer, num_bytes, *socket);
|
net::eth_send(buffer, num_bytes, *socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_hw_rev(buffer: &mut [u8], hw_rev_o: &mut HWRev, socket: &mut SocketHandle) {
|
||||||
|
let hw_rev = HwRevObj {
|
||||||
|
json: HwRevType {
|
||||||
|
msg_type: ResponseEnum::HwRev,
|
||||||
|
hw_rev: *hw_rev_o,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut num_bytes = hw_rev.get_json("/json", buffer).unwrap();
|
||||||
|
buffer[num_bytes] = b'\n';
|
||||||
|
num_bytes += 1;
|
||||||
|
net::eth_send(buffer, num_bytes, *socket);
|
||||||
|
}
|
||||||
// Use a minimal struct for high speed cmd ctrl to reduce processing overhead
|
// Use a minimal struct for high speed cmd ctrl to reduce processing overhead
|
||||||
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
|
||||||
pub struct TecSetICmdJson {
|
pub struct TecSetICmdJson {
|
||||||
|
@ -245,6 +272,7 @@ pub fn execute_cmd(
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
socket: &mut SocketHandle,
|
socket: &mut SocketHandle,
|
||||||
|
hw_rev: &mut HWRev,
|
||||||
laser: &mut LdDrive,
|
laser: &mut LdDrive,
|
||||||
thermostat: &mut Thermostat,
|
thermostat: &mut Thermostat,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
|
@ -296,7 +324,8 @@ pub fn execute_cmd(
|
||||||
unsafe {
|
unsafe {
|
||||||
dfu::set_dfu_trigger();
|
dfu::set_dfu_trigger();
|
||||||
}
|
}
|
||||||
*state = State::HardReset;
|
net::eth_poll_iface();
|
||||||
|
*state = State::PrepareForDfu;
|
||||||
}
|
}
|
||||||
Some(DeviceCmd::SetActiveReportMode) => match cmd.json.data_bool {
|
Some(DeviceCmd::SetActiveReportMode) => match cmd.json.data_bool {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
@ -348,6 +377,9 @@ pub fn execute_cmd(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Some(DeviceCmd::GetHwRev) => {
|
||||||
|
send_hw_rev(buffer, hw_rev, socket);
|
||||||
|
}
|
||||||
Some(DeviceCmd::GetStatusReport) => {
|
Some(DeviceCmd::GetStatusReport) => {
|
||||||
send_status_report(buffer, laser, thermostat, socket);
|
send_status_report(buffer, laser, thermostat, socket);
|
||||||
}
|
}
|
||||||
|
@ -364,6 +396,7 @@ pub fn execute_cmd(
|
||||||
}
|
}
|
||||||
Some(DeviceCmd::HardReset) => {
|
Some(DeviceCmd::HardReset) => {
|
||||||
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
|
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
|
||||||
|
net::eth_poll_iface();
|
||||||
*state = State::PrepareForHardReset;
|
*state = State::PrepareForHardReset;
|
||||||
}
|
}
|
||||||
None => { /* Do Nothing */ }
|
None => { /* Do Nothing */ }
|
||||||
|
|
|
@ -47,6 +47,7 @@ pub struct ServerHandle {
|
||||||
dma: EthernetDMA<'static, 'static>,
|
dma: EthernetDMA<'static, 'static>,
|
||||||
phy: EthernetPhy<EthernetMACWithMii<Pin<'A', 2, Alternate<11>>, Pin<'C', 1, Alternate<11>>>>,
|
phy: EthernetPhy<EthernetMACWithMii<Pin<'A', 2, Alternate<11>>, Pin<'C', 1, Alternate<11>>>>,
|
||||||
link_was_up: bool,
|
link_was_up: bool,
|
||||||
|
data_sent: bool,
|
||||||
}
|
}
|
||||||
pub type EthernetPins = EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
|
pub type EthernetPins = EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
|
||||||
pub struct EthernetMgmtPins {
|
pub struct EthernetMgmtPins {
|
||||||
|
@ -55,7 +56,7 @@ pub struct EthernetMgmtPins {
|
||||||
}
|
}
|
||||||
pub type EthInterface = Interface;
|
pub type EthInterface = Interface;
|
||||||
|
|
||||||
pub const NUM_OF_SOCKETS: usize = 4;
|
pub const NUM_OF_SOCKETS: usize = 4 + 1;
|
||||||
const TCP_BUFFER_SIZE: usize = 4096;
|
const TCP_BUFFER_SIZE: usize = 4096;
|
||||||
static mut RX_RING: Option<[RxRingEntry; 8]> = None;
|
static mut RX_RING: Option<[RxRingEntry; 8]> = None;
|
||||||
static mut TX_RING: Option<[TxRingEntry; 8]> = None;
|
static mut TX_RING: Option<[TxRingEntry; 8]> = None;
|
||||||
|
@ -138,7 +139,7 @@ impl ServerHandle {
|
||||||
let tcp_handles = {
|
let tcp_handles = {
|
||||||
// Do not use NUM_OF_SOCKETS to define array size to
|
// Do not use NUM_OF_SOCKETS to define array size to
|
||||||
// remind developers to create/remove tcp_handles accordingly after changing NUM_OF_SOCKETS
|
// remind developers to create/remove tcp_handles accordingly after changing NUM_OF_SOCKETS
|
||||||
let mut tcp_handles: [MaybeUninit<SocketHandle>; 4] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut tcp_handles: [MaybeUninit<SocketHandle>; 5] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
macro_rules! create_tcp_handle {
|
macro_rules! create_tcp_handle {
|
||||||
($rx_storage:ident, $tx_storage:ident, $handle:expr) => {
|
($rx_storage:ident, $tx_storage:ident, $handle:expr) => {
|
||||||
|
@ -155,8 +156,9 @@ impl ServerHandle {
|
||||||
create_tcp_handle!(RX_STORAGE1, TX_STORAGE1, tcp_handles[1]);
|
create_tcp_handle!(RX_STORAGE1, TX_STORAGE1, tcp_handles[1]);
|
||||||
create_tcp_handle!(RX_STORAGE2, TX_STORAGE2, tcp_handles[2]);
|
create_tcp_handle!(RX_STORAGE2, TX_STORAGE2, tcp_handles[2]);
|
||||||
create_tcp_handle!(RX_STORAGE3, TX_STORAGE3, tcp_handles[3]);
|
create_tcp_handle!(RX_STORAGE3, TX_STORAGE3, tcp_handles[3]);
|
||||||
|
create_tcp_handle!(RX_STORAGE4, TX_STORAGE4, tcp_handles[4]);
|
||||||
|
|
||||||
unsafe { mem::transmute::<_, [SocketHandle; 4]>(tcp_handles) }
|
unsafe { mem::transmute::<_, [SocketHandle; 5]>(tcp_handles) }
|
||||||
};
|
};
|
||||||
|
|
||||||
for i in 0..NUM_OF_SOCKETS {
|
for i in 0..NUM_OF_SOCKETS {
|
||||||
|
@ -189,6 +191,7 @@ impl ServerHandle {
|
||||||
dma: dma,
|
dma: dma,
|
||||||
phy: phy,
|
phy: phy,
|
||||||
link_was_up: false,
|
link_was_up: false,
|
||||||
|
data_sent: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -246,14 +249,17 @@ impl ServerHandle {
|
||||||
pub fn send(&mut self, buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) {
|
pub fn send(&mut self, buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) {
|
||||||
let socket = self.socket_set.get_mut::<Socket>(socket_handles);
|
let socket = self.socket_set.get_mut::<Socket>(socket_handles);
|
||||||
if num_bytes > 0 {
|
if num_bytes > 0 {
|
||||||
|
cortex_m::interrupt::free(|_| {
|
||||||
match socket.send_slice(&buffer[..num_bytes]) {
|
match socket.send_slice(&buffer[..num_bytes]) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
self.data_sent = false;
|
||||||
info!("Enqueued {} bytes.", num_bytes);
|
info!("Enqueued {} bytes.", num_bytes);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
info!("Bytes cannot be sent. Error: {:?}", err)
|
info!("Bytes cannot be sent. Error: {:?}", err)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +439,7 @@ pub fn eth_update_iface_poll_timer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eth_poll_iface() {
|
pub fn eth_poll_iface() {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
server_handle.poll_iface();
|
server_handle.poll_iface();
|
||||||
|
@ -499,6 +505,46 @@ pub fn eth_close_socket(socket_handles: SocketHandle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eth_get_sock_handle(id: usize) -> SocketHandle {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
|
server_handle.socket_handles[id]
|
||||||
|
} else {
|
||||||
|
panic!("eth_get_sock_handle is called before init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eth_close_socket_by_id(id: usize) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
|
server_handle.close_socket(server_handle.socket_handles[id])
|
||||||
|
} else {
|
||||||
|
panic!("eth_close_socket_by_id is called before init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eth_is_data_sent() -> bool {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
|
server_handle.data_sent
|
||||||
|
} else {
|
||||||
|
panic!("eth_is_data_sent is called before init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eth_set_data_sent(val: bool) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
|
server_handle.data_sent = val;
|
||||||
|
} else {
|
||||||
|
panic!("eth_is_data_sent is called before init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn for_each<F: FnMut(SocketHandle, usize)>(mut callback: F) {
|
pub fn for_each<F: FnMut(SocketHandle, usize)>(mut callback: F) {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||||
|
@ -519,6 +565,9 @@ fn ETH() {
|
||||||
if interrupt_reason.rx {
|
if interrupt_reason.rx {
|
||||||
eth_poll_iface();
|
eth_poll_iface();
|
||||||
}
|
}
|
||||||
|
if interrupt_reason.tx {
|
||||||
|
eth_set_data_sent(true);
|
||||||
|
}
|
||||||
debug!("Ethernet Interrupt{:?}", interrupt_reason);
|
debug!("Ethernet Interrupt{:?}", interrupt_reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue