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";
|
||||
|
||||
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;
|
||||
postFixup = ''
|
||||
|
@ -94,7 +94,7 @@
|
|||
buildInputs = with pkgs; [
|
||||
rust openocd dfu-util glibc
|
||||
] ++ (with python3Packages; [
|
||||
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive aenum
|
||||
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive aenum sipyco
|
||||
]);
|
||||
shellHook=
|
||||
''
|
||||
|
|
|
@ -2,147 +2,135 @@ from pprint import pp
|
|||
from driver.kirdy import Kirdy, FilterConfig
|
||||
import signal
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
"""
|
||||
async def enter_dfu_mode(kirdy: Kirdy):
|
||||
"""
|
||||
Enter Device Firmware Upgrade(Dfu) mode
|
||||
Please see README.md for flash instructions.
|
||||
"""
|
||||
def enter_dfu_mode(kirdy: Kirdy):
|
||||
kirdy.device.dfu()
|
||||
kirdy.end_session()
|
||||
Please refer to README.md for firmware update instructions.
|
||||
"""
|
||||
await kirdy.device.dfu()
|
||||
|
||||
"""
|
||||
Configure Kirdy to actively report status
|
||||
async def active_report(kirdy: Kirdy):
|
||||
"""
|
||||
Configure Kirdy to actively report status to connected socket
|
||||
Press Ctrl + C to exit active report mode
|
||||
"""
|
||||
def active_report(kirdy: Kirdy):
|
||||
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():
|
||||
"""
|
||||
async for data in kirdy.report_mode():
|
||||
pp(data)
|
||||
|
||||
"""
|
||||
Configure Kirdy network and board specific transconductance settings.
|
||||
async def device_cfg(kirdy: Kirdy):
|
||||
"""
|
||||
Configure Kirdy's network and board specific transconductance settings.
|
||||
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
|
||||
# PD current range resolution.
|
||||
kirdy.device.set_pd_mon_fin_gain(1.0)
|
||||
kirdy.device.set_pd_mon_transconductance(1/1000.0)
|
||||
await kirdy.device.set_pd_mon_fin_gain(1.0)
|
||||
await kirdy.device.set_pd_mon_transconductance(1/1000.0)
|
||||
|
||||
# Network Settings will be updated on next reboot.
|
||||
kirdy.device.set_ip_settings(
|
||||
await kirdy.device.set_ip_settings(
|
||||
addr="192.168.1.128",
|
||||
port=1337,
|
||||
prefix_len=24,
|
||||
gateway="192.168.1.1"
|
||||
)
|
||||
# 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.
|
||||
"""
|
||||
def ld_thermostat_cfg(kirdy: Kirdy):
|
||||
# Load the laser diode and thermostat settings from flash
|
||||
kirdy.device.restore_settings_from_flash()
|
||||
"""
|
||||
# Load the laser diode and thermostat settings from flash memory.
|
||||
await kirdy.device.restore_settings_from_flash()
|
||||
|
||||
# Power off the laser diode and thermostat and clear alarm (if any)
|
||||
kirdy.laser.set_power_on(False)
|
||||
kirdy.laser.clear_alarm()
|
||||
kirdy.thermostat.set_power_on(False)
|
||||
kirdy.thermostat.clear_alarm()
|
||||
# Power off the laser diode & thermostat and clear any asserted alarm
|
||||
await kirdy.laser.set_power_on(False)
|
||||
await kirdy.laser.clear_alarm()
|
||||
await kirdy.thermostat.set_power_on(False)
|
||||
await kirdy.thermostat.clear_alarm()
|
||||
|
||||
# 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
|
||||
kirdy.laser.set_default_pwr_on(False)
|
||||
kirdy.thermostat.set_default_pwr_on(False)
|
||||
await kirdy.laser.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
|
||||
# Exceeding the measured power limit triggers overpower protection alarm.
|
||||
# The laser diode power will be turned off while the thermostat power remains unchanged.
|
||||
kirdy.laser.set_ld_pwr_limit(0.0)
|
||||
kirdy.laser.set_pd_mon_dark_current(0.0)
|
||||
kirdy.laser.set_pd_mon_responsitivity(0.0)
|
||||
# Exceeding the power limit triggers overpower protection alarm.
|
||||
# The laser diode power will be cut off upon alarm assertion while the thermostat power remains unchanged.
|
||||
await kirdy.laser.set_ld_pwr_limit(0.0)
|
||||
await kirdy.laser.set_pd_mon_dark_current(0.0)
|
||||
await kirdy.laser.set_pd_mon_responsitivity(0.0)
|
||||
|
||||
# Configure the thermostat NTC thermistor parameters.
|
||||
kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||
kirdy.thermostat.set_sh_t0(25)
|
||||
kirdy.thermostat.set_sh_beta(3900)
|
||||
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||
await kirdy.thermostat.set_sh_t0(25)
|
||||
await kirdy.thermostat.set_sh_beta(3900)
|
||||
|
||||
# Configure the thermostat output limits.
|
||||
# The actual output current is limited by the hardware limit set below.
|
||||
kirdy.thermostat.set_tec_max_cooling_i(1.0)
|
||||
kirdy.thermostat.set_tec_max_heating_i(1.0)
|
||||
kirdy.thermostat.set_tec_max_v(4.0)
|
||||
# Configure the thermostat TEC settings.
|
||||
# The actual output current is limited by value set below.
|
||||
await kirdy.thermostat.set_tec_max_cooling_i(1.0)
|
||||
await kirdy.thermostat.set_tec_max_heating_i(1.0)
|
||||
await kirdy.thermostat.set_tec_max_v(4.0)
|
||||
|
||||
# Configure the thermostat temperature monitor limit.
|
||||
# Exceeding the limit will trigger over temperature protection alarm.
|
||||
# The laser diode and thermostat power will be turned off.
|
||||
kirdy.thermostat.set_temp_mon_upper_limit(70)
|
||||
kirdy.thermostat.set_temp_mon_lower_limit(0)
|
||||
# Configure the thermostat temperature monitor limits.
|
||||
# Exceeding the temperature limits trigger over temperature protection alarm.
|
||||
# Both laser diode and thermostat power will be cut off upon alarm assertion.
|
||||
await kirdy.thermostat.set_temp_mon_upper_limit(70)
|
||||
await kirdy.thermostat.set_temp_mon_lower_limit(0)
|
||||
|
||||
# Configure the thermostat PID related parameter.
|
||||
# You can configure the PID parameter with the autotune tool.
|
||||
# Here provides an example if it is configured manually.
|
||||
kirdy.thermostat.set_temperature_setpoint(25)
|
||||
kirdy.thermostat.set_pid_kp(0.15668282198105507)
|
||||
kirdy.thermostat.set_pid_ki(0.002135962407793784)
|
||||
kirdy.thermostat.set_pid_kd(0.829254515277143)
|
||||
kirdy.thermostat.set_pid_output_max(1.0)
|
||||
kirdy.thermostat.set_pid_output_min(-1.0)
|
||||
# Configure the thermostat PID parameters.
|
||||
# You can configure the PID parameter by the included autotune script.
|
||||
await kirdy.thermostat.set_temperature_setpoint(25)
|
||||
await kirdy.thermostat.set_pid_kp(0.15668282198105507)
|
||||
await kirdy.thermostat.set_pid_ki(0.002135962407793784)
|
||||
await kirdy.thermostat.set_pid_kd(0.829254515277143)
|
||||
await kirdy.thermostat.set_pid_output_max(1.0)
|
||||
await kirdy.thermostat.set_pid_output_min(-1.0)
|
||||
|
||||
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
|
||||
# The ADC sampling rate determines the report and pid update rate.
|
||||
# The chosen filter and sampling rate affects the noise of the readings.
|
||||
# For details, please refer to the AD7172 da`tas`heet.
|
||||
kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||
# For details, please refer to the AD7172 datasheet.
|
||||
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||
|
||||
# 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,
|
||||
# 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
|
||||
# or the value set will not be overwritten by PID output.
|
||||
kirdy.thermostat.set_constant_current_control_mode()
|
||||
kirdy.thermostat.set_tec_i_out(0.0)
|
||||
# or the value set will be overwritten by PID output.
|
||||
await kirdy.thermostat.set_constant_current_control_mode()
|
||||
await kirdy.thermostat.set_tec_i_out(0.0)
|
||||
|
||||
# Save the above settings configured into the flash
|
||||
kirdy.device.save_current_settings_to_flash()
|
||||
# Save the current settings to flash memory
|
||||
await kirdy.device.save_current_settings_to_flash()
|
||||
|
||||
# Power on the laser diode and thermostat
|
||||
kirdy.laser.set_power_on(True)
|
||||
kirdy.thermostat.set_power_on(True)
|
||||
await kirdy.laser.set_power_on(True)
|
||||
await kirdy.thermostat.set_power_on(True)
|
||||
|
||||
pp(kirdy.device.get_settings_summary())
|
||||
pp(kirdy.device.get_status_report())
|
||||
pp(await kirdy.device.get_settings_summary())
|
||||
pp(await kirdy.device.get_status_report())
|
||||
|
||||
def main():
|
||||
async def main():
|
||||
kirdy = Kirdy()
|
||||
kirdy.start_session(host='192.168.1.128', port=1337)
|
||||
await kirdy.wait_until_connected()
|
||||
|
||||
while not(kirdy.connected()):
|
||||
pass
|
||||
|
||||
ld_thermostat_cfg(kirdy)
|
||||
# active_report(kirdy)
|
||||
await ld_thermostat_cfg(kirdy)
|
||||
# await active_report(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__":
|
||||
main()
|
||||
asyncio.run(main())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import types
|
||||
import socket
|
||||
import json
|
||||
import logging
|
||||
|
@ -25,6 +26,7 @@ class CmdList:
|
|||
SetPdFinGain = _dt.f32
|
||||
SetPdTransconductance = _dt.f32
|
||||
SetActiveReportMode = _dt.bool
|
||||
GetHwRev = _dt.none
|
||||
GetStatusReport = _dt.none
|
||||
GetSettingsSummary = _dt.none
|
||||
Dfu = _dt.none
|
||||
|
@ -153,15 +155,14 @@ class InvalidCmd(Exception):
|
|||
pass
|
||||
|
||||
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._send_cmd = send_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:
|
||||
socket.inet_aton(addr)
|
||||
|
@ -175,7 +176,7 @@ class Device:
|
|||
if not(isinstance(port, int) and isinstance(prefix_len, int)):
|
||||
raise InvalidDataType
|
||||
|
||||
return self._send_raw_cmd(
|
||||
return await self._send_raw_cmd(
|
||||
{
|
||||
"device_cmd": "SetIPSettings",
|
||||
"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
|
||||
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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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)
|
||||
'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)
|
||||
'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.
|
||||
'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': {
|
||||
'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)
|
||||
'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)
|
||||
},
|
||||
'temperature': 25.03344, # Temperature Readings (Degree Celsius)
|
||||
|
@ -238,15 +256,11 @@ class Device:
|
|||
}
|
||||
}
|
||||
"""
|
||||
if sig is None:
|
||||
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)
|
||||
return await 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
|
||||
|
@ -255,13 +269,13 @@ class Device:
|
|||
'ld_drive_current': { # Laser Diode Output Current(A)
|
||||
'value': 0.0, # Value Set
|
||||
'max': 0.3 # Max Value Settable
|
||||
,
|
||||
},
|
||||
'pd_mon_params': { # Laser Diode Software Current Limit(A)
|
||||
'responsitivity': None, # Value Set
|
||||
'i_dark': 0.0 # Max Value Settable
|
||||
,
|
||||
'ld_pwr_limit': 0.0 # Laser Diode Power Limit(W)
|
||||
'ld_terms_short: False # Is Laser Diode Terminals short? (True/False)
|
||||
},
|
||||
'ld_pwr_limit': 0.0, # Laser Diode Power Limit(W)
|
||||
'ld_terms_short: False, # Is Laser Diode Terminals short? (True/False)
|
||||
},
|
||||
'thermostat': {
|
||||
'default_pwr_on': True, # Power on Thermostat at Startup
|
||||
|
@ -312,106 +326,101 @@ class Device:
|
|||
}
|
||||
}
|
||||
"""
|
||||
if sig is None:
|
||||
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)
|
||||
return await 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
|
||||
put Kirdy into Dfu mode for flashing firmware.
|
||||
Hard reset and put the connected Kirdy into the Dfu mode for firmware update.
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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.
|
||||
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:
|
||||
def __init__(self, send_cmd_handler):
|
||||
self._cmd = CmdList.ld
|
||||
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
|
||||
- on (True/False)
|
||||
"""
|
||||
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:
|
||||
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)
|
||||
"""
|
||||
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.
|
||||
- on (True/False)
|
||||
"""
|
||||
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:
|
||||
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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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,
|
||||
overpower protection is triggered.
|
||||
- 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:
|
||||
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
|
||||
|
@ -419,158 +428,158 @@ class Thermostat:
|
|||
self._send_cmd = send_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
|
||||
"""
|
||||
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:
|
||||
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)
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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)
|
||||
- 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)
|
||||
- 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
|
||||
or your newly set value will be overwritten by PID Controller Output
|
||||
- i_out: A
|
||||
"""
|
||||
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):
|
||||
return self._send_raw_cmd({"tec_set_i": float(i_out)})
|
||||
return await self._send_raw_cmd({"tec_set_i": float(i_out)})
|
||||
else:
|
||||
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.
|
||||
"""
|
||||
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
|
||||
- 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.
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
- 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
|
||||
- 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
|
||||
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
||||
- 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
|
||||
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
|
||||
- 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
|
||||
"""
|
||||
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
|
||||
- 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
|
||||
- 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
|
||||
- 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.
|
||||
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,
|
||||
}
|
||||
|
||||
return self._send_raw_cmd(cmd)
|
||||
|
||||
return await self._send_raw_cmd(cmd)
|
||||
class Kirdy:
|
||||
def __init__(self):
|
||||
self.device = Device(self._send_cmd_handler, self._send_raw_cmd_handler, self._get_msg)
|
||||
self.laser = Laser(self._send_cmd_handler)
|
||||
self.thermostat = Thermostat(self._send_cmd_handler, self._send_raw_cmd_handler)
|
||||
self.device = Device(self._send_cmd, self._send_raw_cmd)
|
||||
self.laser = Laser(self._send_cmd)
|
||||
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._event_loop = None
|
||||
|
||||
self._lock = asyncio.Lock()
|
||||
self._msg_queue_get_report = 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
|
||||
self._report_sig = None # Dict
|
||||
self._connected_sig = None # Bool
|
||||
|
||||
self.connected_event = None
|
||||
|
||||
def get_hw_rev(self):
|
||||
return self.hw_rev
|
||||
|
||||
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
|
||||
|
||||
|
@ -625,40 +643,41 @@ class Kirdy:
|
|||
"""
|
||||
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.
|
||||
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 thread retries TCP connection indefinitely.
|
||||
|
||||
In case of disconnection, all the queued tasks are cleared and the handler task retries TCP connection indefinitely.
|
||||
- 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._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._thread = Thread(target=self._event_loop.run_forever)
|
||||
self._thread.start()
|
||||
asyncio.run_coroutine_threadsafe(self._handler(), self._event_loop)
|
||||
return True
|
||||
else:
|
||||
logging.warning("Helper Thread has been started.")
|
||||
return False
|
||||
self._event_loop.run_forever()
|
||||
self.connected_event = asyncio.Event()
|
||||
self.handler_task = asyncio.create_task(self._handler())
|
||||
|
||||
def end_session(self, block=False):
|
||||
async def end_session(self, block=False):
|
||||
"""
|
||||
Stop Kirdy's TCP connection and its associated thread.
|
||||
"""
|
||||
if self._event_loop is not None:
|
||||
if block:
|
||||
while not(self._task_queue.empty()):
|
||||
pass
|
||||
cancel_task = asyncio.run_coroutine_threadsafe(self._stop_handler(), self._event_loop)
|
||||
while not(cancel_task.done()):
|
||||
pass
|
||||
self._thread.join()
|
||||
await self._task_queue.join()
|
||||
|
||||
if self.read_response_task is not None:
|
||||
self.read_response_task.cancel()
|
||||
await self.read_response_task
|
||||
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
|
||||
|
||||
if self._connected_sig is not None:
|
||||
|
@ -668,7 +687,7 @@ class Kirdy:
|
|||
"""
|
||||
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):
|
||||
"""
|
||||
|
@ -676,28 +695,43 @@ class Kirdy:
|
|||
"""
|
||||
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():
|
||||
self._report_mode_on = True
|
||||
|
||||
self.device.set_active_report_mode(True)
|
||||
await self.device.set_active_report_mode(True)
|
||||
report = None
|
||||
|
||||
while self._report_mode_on:
|
||||
report = self._report_queue.get()
|
||||
if isinstance(report, Exception):
|
||||
raise report
|
||||
report = await self._report_queue.get()
|
||||
if not(isinstance(report, dict)):
|
||||
self.stop_active_report()
|
||||
else:
|
||||
yield report
|
||||
|
||||
self.device.set_active_report_mode(False)
|
||||
if isinstance(report, dict):
|
||||
await self.device.set_active_report_mode(False)
|
||||
else:
|
||||
raise ConnectionError
|
||||
|
||||
def stop_report_mode(self):
|
||||
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):
|
||||
# Reader needn't be closed
|
||||
try:
|
||||
|
@ -709,11 +743,6 @@ class Kirdy:
|
|||
self._reader = 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):
|
||||
if self._report_queue.full():
|
||||
self._report_queue.get_nowait()
|
||||
|
@ -724,97 +753,93 @@ class Kirdy:
|
|||
|
||||
async def _handler(self):
|
||||
try:
|
||||
state = State.disconnected
|
||||
self._state = State.disconnected
|
||||
first_con = True
|
||||
task = None
|
||||
while True:
|
||||
if state == State.disconnected:
|
||||
if self._state == State.disconnected:
|
||||
try:
|
||||
self.hw_rev = None
|
||||
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
|
||||
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 = State.connected
|
||||
self._state = State.connected
|
||||
except (OSError, TimeoutError):
|
||||
if first_con:
|
||||
first_con = False
|
||||
logging.warning("Cannot connect to %s:%d. Retrying in the background", self._host, self._ctrl_port)
|
||||
await asyncio.sleep(self._con_retry)
|
||||
logging.warning("Cannot connect to %s:%d. Retrying in the background.", self._host, self._ctrl_port)
|
||||
await asyncio.sleep(5.0)
|
||||
|
||||
elif state == State.connected:
|
||||
elif self._state == State.connected:
|
||||
try:
|
||||
task = await self._task_queue.get()
|
||||
if isinstance(task, Exception):
|
||||
raise task
|
||||
await task()
|
||||
self._task_queue.task_done()
|
||||
|
||||
response = await asyncio.wait_for(task(), self._timeout)
|
||||
if response is not None:
|
||||
if response["msg_type"] != "Acknowledge":
|
||||
self._msg_queue.put_nowait(response)
|
||||
|
||||
except (TimeoutError, ConnectionResetError):
|
||||
logging.warning("Kirdy connection is dropped.")
|
||||
except (TimeoutError, ConnectionResetError, ConnectionError):
|
||||
logging.warning("Connection to Kirdy is dropped.")
|
||||
first_con = True
|
||||
read_response_fut.cancel()
|
||||
await self._sock_disconnection_handling()
|
||||
self.read_response_task.cancel()
|
||||
|
||||
# State Transition
|
||||
state = State.disconnected
|
||||
|
||||
except asyncio.exceptions.CancelledError:
|
||||
logging.debug("Handler is canceling")
|
||||
except:
|
||||
logging.debug("Handler experienced an error. Exiting.", exc_info=True)
|
||||
self._state = State.disconnected
|
||||
await self._sock_disconnection_handling()
|
||||
except asyncio.exceptions.CancelledError:
|
||||
pass
|
||||
except:
|
||||
logging.warning("Handler experienced an error.", exc_info=True)
|
||||
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):
|
||||
try:
|
||||
while True:
|
||||
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:
|
||||
responses = await self._read_response()
|
||||
for response in responses:
|
||||
response = await self._read_response()
|
||||
|
||||
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 self._report_sig is None:
|
||||
if self._report_queue.full():
|
||||
self._report_queue.get_nowait()
|
||||
self._report_queue.put_nowait(response)
|
||||
self._report_queue.put_nowait_overwrite(response)
|
||||
else:
|
||||
self._report_sig.emit(response)
|
||||
else:
|
||||
if self._msg_queue_get_report:
|
||||
async with self._lock:
|
||||
self._msg_queue_get_report = False
|
||||
if self._int_msg_queue.full():
|
||||
logging.debug("_int_msg_queue is full")
|
||||
self._int_msg_queue.get_nowait()
|
||||
self._int_msg_queue.put_nowait(response)
|
||||
self._int_msg_queue.put_nowait_overwrite(response)
|
||||
except asyncio.exceptions.CancelledError:
|
||||
logging.debug("Read Response Handler is canceling")
|
||||
except TimeoutError:
|
||||
logging.warning("Read active report response timeout")
|
||||
if self._task_queue.full():
|
||||
logging.debug("_int_msg_queue is full")
|
||||
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:
|
||||
pass
|
||||
except (TimeoutError, ConnectionResetError, ConnectionError) as exec:
|
||||
self._task_queue.put_nowait_overwrite(exec)
|
||||
self._int_msg_queue.put_nowait_overwrite(exec)
|
||||
except Exception as exec:
|
||||
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:
|
||||
self._report_mode_on = False
|
||||
self._report_queue.put_nowait(TimeoutError)
|
||||
self._report_queue.put_nowait_overwrite(TimeoutError)
|
||||
|
||||
async def _stop_handler(self):
|
||||
for task in asyncio.all_tasks():
|
||||
|
@ -822,71 +847,52 @@ class Kirdy:
|
|||
await asyncio.gather(*asyncio.all_tasks(), loop=self._event_loop)
|
||||
|
||||
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._msg_queue = queue.Queue(maxsize=64)
|
||||
self._report_queue = queue.Queue(maxsize=16)
|
||||
self._report_queue = asyncio.Queue(maxsize=16)
|
||||
|
||||
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.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
|
||||
if self._connected_sig is not None:
|
||||
self._connected_sig.emit(True)
|
||||
|
||||
async def _read_response(self, buffer_size=16384):
|
||||
async def _read_response(self):
|
||||
raw_response = b''
|
||||
while len(raw_response) == 0:
|
||||
# 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 = response[:-1]
|
||||
return json.loads(response[0])
|
||||
|
||||
items = []
|
||||
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):
|
||||
async def _send_raw_cmd(self, cmd, msg_type="Acknowledge", sig=None):
|
||||
if self.connected():
|
||||
async with self._lock:
|
||||
self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
|
||||
await self._writer.drain()
|
||||
response = await self._int_msg_queue.get()
|
||||
if response["msg_type"] == msg_type:
|
||||
msg = await asyncio.wait_for(self._int_msg_queue.get(), self._timeout)
|
||||
|
||||
if msg["msg_type"] == msg_type:
|
||||
if sig is not None:
|
||||
sig.emit(response)
|
||||
sig.emit(msg)
|
||||
return {"msg_type": "Acknowledge"}
|
||||
return response
|
||||
return msg
|
||||
else:
|
||||
raise InvalidCmd
|
||||
else:
|
||||
raise ConnectionError
|
||||
|
||||
def _send_cmd_handler(self, target, cmd, data=None, msg_type="Acknowledge", sig=None, hard_reset=False):
|
||||
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):
|
||||
async def _send_cmd(self, target, cmd, data=None, msg_type="Acknowledge", sig=None):
|
||||
cmd_dict = {}
|
||||
cmd_dict[target] = cmd.name
|
||||
|
||||
|
@ -904,17 +910,21 @@ class Kirdy:
|
|||
pass
|
||||
|
||||
if msg_type == 'Report':
|
||||
async with self._lock:
|
||||
self._msg_queue_get_report = True
|
||||
|
||||
async with self._lock:
|
||||
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
|
||||
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 sig is not None:
|
||||
sig.emit(msg)
|
||||
return {"msg_type": "Acknowledge"}
|
||||
else:
|
||||
return msg
|
||||
else:
|
||||
raise InvalidCmd
|
||||
|
|
|
@ -20,7 +20,7 @@ import logging
|
|||
import asyncio
|
||||
from driver.kirdy import Kirdy as Kirdy_Driver
|
||||
import qasync
|
||||
from qasync import asyncClose
|
||||
from qasync import asyncClose, asyncSlot
|
||||
from collections import deque
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from time import time
|
||||
|
@ -96,15 +96,16 @@ class Kirdy(QObject):
|
|||
def end_session(self):
|
||||
if self._timer.isActive():
|
||||
self._timer.stop()
|
||||
self._kirdy.end_session()
|
||||
asyncio.get_running_loop().create_task(self._kirdy.end_session())
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def connected_setup(self, 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):
|
||||
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)
|
||||
def start_polling(self, start):
|
||||
|
@ -128,6 +129,9 @@ class Kirdy(QObject):
|
|||
else:
|
||||
logging.debug("Attempt to update polling timer when it is stopped")
|
||||
|
||||
def get_hw_rev(self):
|
||||
return self._kirdy.get_hw_rev()
|
||||
|
||||
class Graphs:
|
||||
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]
|
||||
|
@ -524,12 +528,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
def setup_menu_bar(self):
|
||||
@pyqtSlot(bool)
|
||||
def about_kirdy(_):
|
||||
# TODO: Replace the hardware revision placeholder
|
||||
hw_rev = self.kirdy_handler.get_hw_rev()
|
||||
QtWidgets.QMessageBox.about(
|
||||
self,
|
||||
"About Kirdy",
|
||||
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)
|
||||
|
@ -548,19 +552,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
|
||||
@pyqtSlot(bool)
|
||||
def dfu_mode(_):
|
||||
self.kirdy.device.dfu()
|
||||
self.kirdy.task_dispatcher(self.kirdy.device.dfu())
|
||||
self.kirdy_handler.end_session()
|
||||
self.menu_action_dfu_mode.triggered.connect(dfu_mode)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def reset_kirdy(_):
|
||||
self.kirdy.device.hard_reset()
|
||||
self.kirdy.task_dispatcher(self.kirdy.device.hard_reset())
|
||||
self.kirdy_handler.end_session()
|
||||
self.menu_action_hard_reset.triggered.connect(reset_kirdy)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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.setWindowTitle("Config saved")
|
||||
saved.setText(f"Laser diode and thermostat configs have been saved into flash.")
|
||||
|
@ -570,7 +574,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
|
||||
@pyqtSlot(bool)
|
||||
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.setWindowTitle("Config loaded")
|
||||
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):
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
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)
|
||||
|
||||
def _set_up_plot_menu(self):
|
||||
|
@ -665,11 +669,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
tree.setParameters(self.params[3], showTop=False)
|
||||
self.params[3].sigTreeStateChanged.connect(self.send_command)
|
||||
|
||||
@pyqtSlot()
|
||||
def autotune(param):
|
||||
@asyncSlot()
|
||||
async def autotune(param):
|
||||
match self.autotuner.state():
|
||||
case PIDAutotuneState.STATE_OFF:
|
||||
settings = self.kirdy.device.get_settings_summary()
|
||||
settings = await self.kirdy.device.get_settings_summary()
|
||||
self.autotuner.setParam(
|
||||
param.parent().child('Target Temperature').value(),
|
||||
param.parent().child('Test Current').value() / 1000,
|
||||
|
@ -678,7 +682,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
param.parent().child('Lookback').value())
|
||||
self.autotuner.setReady()
|
||||
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.loading_spinner.show()
|
||||
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:
|
||||
self.autotuner.setOff()
|
||||
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.background_task_lbl.setText("Ready.")
|
||||
self.loading_spinner.stop()
|
||||
|
@ -698,16 +702,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
match self.autotuner.state():
|
||||
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
||||
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:
|
||||
kp, ki, kd = self.autotuner.get_tec_pid()
|
||||
self.autotuner.setOff()
|
||||
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
|
||||
self.kirdy.thermostat.set_pid_kp(kp)
|
||||
self.kirdy.thermostat.set_pid_ki(ki)
|
||||
self.kirdy.thermostat.set_pid_kd(kd)
|
||||
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_pid_kp(kp))
|
||||
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki))
|
||||
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd))
|
||||
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode())
|
||||
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.background_task_lbl.setText("Ready.")
|
||||
self.loading_spinner.stop()
|
||||
|
@ -719,7 +723,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
case PIDAutotuneState.STATE_FAILED:
|
||||
self.autotuner.setOff()
|
||||
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.background_task_lbl.setText("Ready.")
|
||||
self.loading_spinner.stop()
|
||||
|
@ -768,7 +772,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
if result:
|
||||
self.connect_btn.setText("Disconnect")
|
||||
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
||||
# TODO: self.hw_rev_data = self.kirdy.hw_rev()
|
||||
self._status()
|
||||
else:
|
||||
if self.kirdy_handler.connecting():
|
||||
|
@ -782,10 +785,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
self.connect_btn.clicked.connect(self.kirdy_handler.end_session)
|
||||
|
||||
def _status(self):
|
||||
# TODO: Get rev no from Kirdy and then add revision into the text
|
||||
host = self.ip_addr
|
||||
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):
|
||||
self.graphs.clear_data_pts()
|
||||
|
@ -899,7 +902,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
port = net_settings["port"]
|
||||
prefix_len = net_settings["prefix_len"]
|
||||
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")
|
||||
|
||||
@pyqtSlot()
|
||||
|
@ -931,7 +934,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
target, action = inner_param.opts['target_action_pair'][inner_param.opts['limits'].index(data)]
|
||||
cmd = getattr(getattr(self.kirdy, target), action)
|
||||
param.child(*param.childPath(inner_param)).setOpts(lock=True)
|
||||
cmd()
|
||||
self.kirdy.task_dispatcher(cmd())
|
||||
param.child(*param.childPath(inner_param)).setOpts(lock=False)
|
||||
continue
|
||||
""" 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)
|
||||
cmd = getattr(getattr(self.kirdy, inner_param.opts["target"]), inner_param.opts["action"])
|
||||
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)
|
||||
continue
|
||||
|
||||
def coro_main():
|
||||
async def coro_main():
|
||||
args = get_argparser().parse_args()
|
||||
if 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.show()
|
||||
|
||||
app.aboutToQuit.connect(main_window.kirdy_handler.end_session)
|
||||
app.exec()
|
||||
await app_quit_event.wait()
|
||||
|
||||
def main():
|
||||
coro_main()
|
||||
qasync.run(coro_main())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -8,6 +8,7 @@ import time
|
|||
import signal
|
||||
from driver.kirdy import Kirdy, FilterConfig
|
||||
import asyncio
|
||||
from sipyco.asyncio_tools import SignalHandler
|
||||
|
||||
# Based on hirshmann pid-autotune libiary
|
||||
# See https://github.com/hirschmann/pid-autotune
|
||||
|
@ -76,6 +77,10 @@ class PIDAutotune:
|
|||
def setOff(self):
|
||||
self._state = PIDAutotuneState.STATE_OFF
|
||||
|
||||
def setFailed(self):
|
||||
self._state = PIDAutotuneState.STATE_FAILED
|
||||
self._peak_count = 30
|
||||
|
||||
def state(self):
|
||||
"""Get the current state."""
|
||||
return self._state
|
||||
|
@ -246,7 +251,7 @@ class PIDAutotune:
|
|||
return False
|
||||
|
||||
|
||||
def main():
|
||||
async def main():
|
||||
"""
|
||||
PID AutoTune Tools for Kirdy
|
||||
The obtained temperature works best at the target temperature specified.
|
||||
|
@ -271,46 +276,46 @@ def main():
|
|||
noiseband = 2.0
|
||||
|
||||
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()):
|
||||
pass
|
||||
|
||||
kirdy.laser.set_power_on(False)
|
||||
kirdy.laser.set_i(0)
|
||||
await kirdy.laser.set_power_on(False)
|
||||
await kirdy.laser.set_i(0)
|
||||
|
||||
kirdy.thermostat.set_power_on(False)
|
||||
kirdy.thermostat.set_constant_current_control_mode()
|
||||
kirdy.thermostat.set_tec_i_out(0)
|
||||
kirdy.thermostat.clear_alarm()
|
||||
await kirdy.thermostat.set_power_on(False)
|
||||
await kirdy.thermostat.set_constant_current_control_mode()
|
||||
await kirdy.thermostat.set_tec_i_out(0)
|
||||
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.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
|
||||
kirdy.thermostat.set_sh_beta(3950)
|
||||
kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||
kirdy.thermostat.set_sh_t0(25)
|
||||
await kirdy.thermostat.set_sh_beta(3950)
|
||||
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
|
||||
await kirdy.thermostat.set_sh_t0(25)
|
||||
|
||||
# Set a large enough temperature range so that it won't trigger overtemperature protection
|
||||
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_upper_limit(target_temperature + 20)
|
||||
await kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20)
|
||||
|
||||
kirdy.thermostat.set_tec_max_cooling_i(output_step)
|
||||
kirdy.thermostat.set_tec_max_heating_i(output_step)
|
||||
await kirdy.thermostat.set_tec_max_cooling_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
|
||||
kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||
settings = kirdy.device.get_settings_summary()
|
||||
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
||||
settings = await kirdy.device.get_settings_summary()
|
||||
sampling_rate = settings["thermostat"]["temp_adc_settings"]["rate"]
|
||||
|
||||
print("Settings: {0}".format(settings))
|
||||
|
@ -318,10 +323,10 @@ def main():
|
|||
tuner = PIDAutotune(target_temperature, output_step,
|
||||
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:
|
||||
status_report = kirdy.device.get_status_report()
|
||||
while True:
|
||||
status_report = await kirdy.device.get_status_report()
|
||||
|
||||
temperature = status_report["thermostat"]["temperature"]
|
||||
ts = status_report['ts']
|
||||
|
@ -332,19 +337,20 @@ def main():
|
|||
break
|
||||
|
||||
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)
|
||||
kirdy.thermostat.set_power_on(False)
|
||||
await kirdy.thermostat.set_tec_i_out(0)
|
||||
await kirdy.thermostat.set_power_on(False)
|
||||
|
||||
if tuner.state() == PIDAutotuneState.STATE_SUCCEEDED:
|
||||
pid_params = tuner.get_pid_parameters(tuning_rule="tyreus-luyben")
|
||||
kirdy.thermostat.set_pid_kp(pid_params.Kp)
|
||||
kirdy.thermostat.set_pid_ki(pid_params.Ki)
|
||||
kirdy.thermostat.set_pid_kd(pid_params.Kd)
|
||||
kirdy.thermostat.set_pid_output_max(1.0)
|
||||
kirdy.thermostat.set_pid_output_min(1.0)
|
||||
await kirdy.thermostat.set_pid_kp(pid_params.Kp)
|
||||
await kirdy.thermostat.set_pid_ki(pid_params.Ki)
|
||||
await kirdy.thermostat.set_pid_kd(pid_params.Kd)
|
||||
await kirdy.thermostat.set_pid_output_max(1.0)
|
||||
await kirdy.thermostat.set_pid_output_min(1.0)
|
||||
|
||||
kirdy.end_session(block=True)
|
||||
await kirdy.end_session()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
asyncio.run(main())
|
||||
|
|
|
@ -9,7 +9,8 @@ use uom::si::{electric_current::{ampere, milliampere},
|
|||
f32::ElectricCurrent};
|
||||
|
||||
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::*},
|
||||
net::net::{IpSettings, ServerHandle},
|
||||
thermostat::{max1968::MAX1968, thermostat::Thermostat},
|
||||
|
@ -23,7 +24,7 @@ const WATCHDOG_PERIOD: u32 = 30000;
|
|||
pub fn bootup(
|
||||
mut core_perif: CorePeripherals,
|
||||
perif: Peripherals,
|
||||
) -> (IndependentWatchdog, FlashStore, LdDrive, Thermostat) {
|
||||
) -> (IndependentWatchdog, FlashStore, HWRev, LdDrive, Thermostat) {
|
||||
core_perif.SCB.enable_icache();
|
||||
core_perif.SCB.enable_dcache(&mut core_perif.CPUID);
|
||||
|
||||
|
@ -119,5 +120,5 @@ pub fn bootup(
|
|||
|
||||
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 miniconf::Tree;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stm32f4xx_hal::{gpio::{Input, PE10, PE11, PE8, PE9},
|
||||
signature};
|
||||
|
||||
|
@ -11,6 +13,7 @@ pub struct HwRevPins {
|
|||
pub h3: PE11<Input>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||
pub struct HWRev {
|
||||
pub major: 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_std)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::{marker::PhantomData, u32};
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use log::{debug, info};
|
||||
|
@ -52,6 +52,7 @@ pub enum State {
|
|||
SaveLdThermostatSettings,
|
||||
SaveDeviceSettings,
|
||||
PrepareForHardReset,
|
||||
PrepareForDfu,
|
||||
HardReset,
|
||||
}
|
||||
|
||||
|
@ -64,7 +65,7 @@ fn main() -> ! {
|
|||
let core_perif = CorePeripherals::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 {
|
||||
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 mut sock_ts: [u32; net::net::NUM_OF_SOCKETS] = [0; net::net::NUM_OF_SOCKETS];
|
||||
|
||||
loop {
|
||||
wd.feed();
|
||||
|
||||
|
@ -183,6 +186,10 @@ fn main() -> ! {
|
|||
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| {
|
||||
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) {
|
||||
|
@ -195,6 +202,7 @@ fn main() -> ! {
|
|||
eth_data_buffer,
|
||||
bytes,
|
||||
&mut socket,
|
||||
&mut hw_rev,
|
||||
&mut laser,
|
||||
&mut thermostat,
|
||||
&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 Transition
|
||||
|
@ -261,9 +296,30 @@ fn main() -> ! {
|
|||
None,
|
||||
&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 => {
|
||||
wd.feed();
|
||||
laser.power_down();
|
||||
|
|
|
@ -10,7 +10,7 @@ use uom::si::{electric_current::{ampere, ElectricCurrent},
|
|||
electrical_resistance::{ohm, ElectricalResistance},
|
||||
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},
|
||||
pd_mon_params::{self, ResponsitivityUnit}},
|
||||
net::net,
|
||||
|
@ -26,10 +26,13 @@ pub enum ResponseEnum {
|
|||
Reserved,
|
||||
Settings,
|
||||
Report,
|
||||
HwRev,
|
||||
Acknowledge,
|
||||
InvalidDatatype,
|
||||
InvalidCmd,
|
||||
HardReset,
|
||||
Dfu,
|
||||
ConnectionClose,
|
||||
}
|
||||
|
||||
pub type MsgType = Option<&'static str>;
|
||||
|
@ -59,6 +62,7 @@ pub struct ResponseObj<'a> {
|
|||
enum DeviceCmd {
|
||||
#[default]
|
||||
Reserved,
|
||||
GetHwRev,
|
||||
SetIPSettings,
|
||||
SetActiveReportMode,
|
||||
SetPdFinGain,
|
||||
|
@ -173,6 +177,17 @@ pub struct SettingsSummaryObj {
|
|||
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) {
|
||||
let response = ResponseObj {
|
||||
json: Response {
|
||||
|
@ -227,6 +242,18 @@ pub fn send_status_report(
|
|||
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
|
||||
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
|
||||
pub struct TecSetICmdJson {
|
||||
|
@ -245,6 +272,7 @@ pub fn execute_cmd(
|
|||
buffer: &mut [u8],
|
||||
buffer_size: usize,
|
||||
socket: &mut SocketHandle,
|
||||
hw_rev: &mut HWRev,
|
||||
laser: &mut LdDrive,
|
||||
thermostat: &mut Thermostat,
|
||||
state: &mut State,
|
||||
|
@ -296,7 +324,8 @@ pub fn execute_cmd(
|
|||
unsafe {
|
||||
dfu::set_dfu_trigger();
|
||||
}
|
||||
*state = State::HardReset;
|
||||
net::eth_poll_iface();
|
||||
*state = State::PrepareForDfu;
|
||||
}
|
||||
Some(DeviceCmd::SetActiveReportMode) => match cmd.json.data_bool {
|
||||
Some(val) => {
|
||||
|
@ -348,6 +377,9 @@ pub fn execute_cmd(
|
|||
);
|
||||
}
|
||||
},
|
||||
Some(DeviceCmd::GetHwRev) => {
|
||||
send_hw_rev(buffer, hw_rev, socket);
|
||||
}
|
||||
Some(DeviceCmd::GetStatusReport) => {
|
||||
send_status_report(buffer, laser, thermostat, socket);
|
||||
}
|
||||
|
@ -364,6 +396,7 @@ pub fn execute_cmd(
|
|||
}
|
||||
Some(DeviceCmd::HardReset) => {
|
||||
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
|
||||
net::eth_poll_iface();
|
||||
*state = State::PrepareForHardReset;
|
||||
}
|
||||
None => { /* Do Nothing */ }
|
||||
|
|
|
@ -47,6 +47,7 @@ pub struct ServerHandle {
|
|||
dma: EthernetDMA<'static, 'static>,
|
||||
phy: EthernetPhy<EthernetMACWithMii<Pin<'A', 2, Alternate<11>>, Pin<'C', 1, Alternate<11>>>>,
|
||||
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 struct EthernetMgmtPins {
|
||||
|
@ -55,7 +56,7 @@ pub struct EthernetMgmtPins {
|
|||
}
|
||||
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;
|
||||
static mut RX_RING: Option<[RxRingEntry; 8]> = None;
|
||||
static mut TX_RING: Option<[TxRingEntry; 8]> = None;
|
||||
|
@ -138,7 +139,7 @@ impl ServerHandle {
|
|||
let tcp_handles = {
|
||||
// Do not use NUM_OF_SOCKETS to define array size to
|
||||
// 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 {
|
||||
($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_STORAGE2, TX_STORAGE2, tcp_handles[2]);
|
||||
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 {
|
||||
|
@ -189,6 +191,7 @@ impl ServerHandle {
|
|||
dma: dma,
|
||||
phy: phy,
|
||||
link_was_up: false,
|
||||
data_sent: true,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
|
@ -246,14 +249,17 @@ impl ServerHandle {
|
|||
pub fn send(&mut self, buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) {
|
||||
let socket = self.socket_set.get_mut::<Socket>(socket_handles);
|
||||
if num_bytes > 0 {
|
||||
cortex_m::interrupt::free(|_| {
|
||||
match socket.send_slice(&buffer[..num_bytes]) {
|
||||
Ok(_) => {
|
||||
self.data_sent = false;
|
||||
info!("Enqueued {} bytes.", num_bytes);
|
||||
}
|
||||
Err(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 {
|
||||
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||
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) {
|
||||
unsafe {
|
||||
if let Some(ref mut server_handle) = SERVER_HANDLE {
|
||||
|
@ -519,6 +565,9 @@ fn ETH() {
|
|||
if interrupt_reason.rx {
|
||||
eth_poll_iface();
|
||||
}
|
||||
if interrupt_reason.tx {
|
||||
eth_set_data_sent(true);
|
||||
}
|
||||
debug!("Ethernet Interrupt{:?}", interrupt_reason);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue