This commit is contained in:
Thomas Muller 2022-07-14 00:07:59 -04:00
commit 4f359e604a
Signed by: thomas
GPG key ID: AF006EB730564952
12 changed files with 1051 additions and 0 deletions

4
.idea/encodings.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

13
.idea/hit.iml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

7
.idea/misc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (hit)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/hit.iml" filepath="$PROJECT_DIR$/.idea/hit.iml" />
</modules>
</component>
</project>

271
.idea/workspace.xml Normal file
View file

@ -0,0 +1,271 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="74089eaa-e081-4df7-9c12-fd404f4aba9c" name="Default Changelist" comment="" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CoverageDataManager">
<SUITE FILE_PATH="coverage/hit$leds.coverage" NAME="leds Coverage Results" MODIFIED="1553307066056" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/hit$main.coverage" NAME="main Coverage Results" MODIFIED="1553393814749" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/main.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="510">
<caret line="42" column="55" selection-start-line="42" selection-start-column="55" selection-end-line="42" selection-end-column="55" />
<folding>
<element signature="e#0#10#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/settings.json">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/leds.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="518">
<caret line="85" column="60" selection-start-line="85" selection-start-column="40" selection-end-line="85" selection-end-column="60" />
<folding>
<element signature="e#0#14#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>Swithc</find>
<find>Qtech</find>
</findStrings>
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/leds.py" />
<option value="$PROJECT_DIR$/main.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="1992" />
<option name="width" value="1272" />
<option name="height" value="700" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="hit" type="b2602c69:ProjectViewProjectNode" />
<item name="hit" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Python.main">
<configuration name="leds" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="hit" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/leds.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="main" type="PythonConfigurationType" factoryName="Python">
<module name="hit" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<list>
<item itemvalue="Python.main" />
<item itemvalue="Python.leds" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.leds" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="74089eaa-e081-4df7-9c12-fd404f4aba9c" name="Default Changelist" comment="" />
<created>1553146393337</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1553146393337</updated>
<workItem from="1553146396302" duration="955000" />
<workItem from="1553192418554" duration="36000" />
<workItem from="1553208814453" duration="7000" />
<workItem from="1553291403133" duration="660000" />
<workItem from="1553299189331" duration="12875000" />
<workItem from="1553314603230" duration="697000" />
<workItem from="1553315321224" duration="5682000" />
<workItem from="1553393747744" duration="683000" />
<workItem from="1553395471155" duration="986000" />
<workItem from="1553475449472" duration="47000" />
<workItem from="1554003982369" duration="4238000" />
<workItem from="1555360955184" duration="611000" />
<workItem from="1555370200529" duration="667000" />
<workItem from="1556855475859" duration="72000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="28216000" />
</component>
<component name="ToolWindowManager">
<frame x="1356" y="0" width="1928" height="1060" extended-state="6" />
<editor active="true" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.2886873" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" weight="0.32903227" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Version Control" order="8" />
<window_info anchor="bottom" id="Database Changes" order="9" />
<window_info anchor="bottom" id="Event Log" order="10" side_tool="true" />
<window_info anchor="bottom" id="Terminal" order="11" />
<window_info anchor="bottom" id="Python Console" order="12" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="SciView" order="3" />
<window_info anchor="right" id="Database" order="4" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/venv/lib/python3.6/site-packages/paho/mqtt/client.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="190">
<caret line="1577" selection-start-line="1577" selection-end-line="1577" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/hit.ui">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="390">
<caret line="26" column="10" selection-start-line="26" selection-start-column="10" selection-end-line="26" selection-end-column="10" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/main.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="510">
<caret line="42" column="55" selection-start-line="42" selection-start-column="55" selection-end-line="42" selection-end-column="55" />
<folding>
<element signature="e#0#10#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/settings.json">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/leds.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="518">
<caret line="85" column="60" selection-start-line="85" selection-start-column="40" selection-end-line="85" selection-end-column="60" />
<folding>
<element signature="e#0#14#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

Binary file not shown.

Binary file not shown.

168
agpib.py Normal file
View file

@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012,2013 Thibault VINCENT <tibal@reloaded.fr>
#
# This file is part of Agpib.
#
# Agpib is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Agpib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Agpib. If not, see <http://www.gnu.org/licenses/>.
#
import re
from arduino import Arduino, ArduinoError
class AgpibError(Exception):
pass
class Agpib(Arduino):
CMD_OUT = {
'PING' : 0x00,
'STATUS' : 0x01,
'INIT' : 0x02,
'LOCKREAD' : 0x03,
'CONTROLLER': 0x04,
'REMOTE' : 0x05,
'TALKER' : 0x06,
'LISTENER' : 0x07,
'UNLISTEN' : 0x08,
'UNTALK' : 0x09,
'LOCKOUT' : 0x0a,
'CLEAR' : 0x0b,
'UNLOCK' : 0x0c,
'TRIGGER' : 0x0d,
'READ' : 0x0e,
'WRITE' : 0x0f,
'CMD' : 0x10,
'ENGAGE_REQ': 0x11,
}
CMD_IN = {
'PONG' : 0x00,
'CHUNK' : 0x01,
'STRING' : 0x02,
'REQUEST' : 0x03,
}
FLAGS = {
'BOOLEAN' : 0x1,
}
def interface_ping(self):
self._write_command('PING')
try:
resp = self._read_command(timeout=1)
except ArduinoError:
return False
return resp[0] == 'PONG'
def status(self):
self._write_command('STATUS')
resp = self._read_command()
if resp[0] != 'STRING':
raise AgpibError('not getting a string in response of'
' bus status error but: %s' % resp[0])
pattern = '^E(\d)D(\d)N(\d)n(\d)I(\d)S(\d)A(\d)R(\d)(\d{8})$'
labels = ('EOI', 'DAV', 'NRFD', 'NDAC', 'IFC', 'SRQ', 'ATN',
'REN', 'DIO')
match = re.match(pattern, self._read_line().decode('UTF-8'))
return dict(list(zip(labels, match.groups())))
def init(self, address=0x00, controller=True):
self._write_command('INIT')
self._write(chr(address))
self._address = address
if controller:
self._write_command('CONTROLLER')
def lock_read(self, state):
flags = ['BOOLEAN'] if state else []
self._write_command('LOCKREAD', flags=flags)
def remote(self, state):
flags = ['BOOLEAN'] if state else []
self._write_command('REMOTE', flags=flags)
def talker(self, address):
self._write_command('TALKER')
self._write(chr(address & 0x1f))
def listener(self, address):
self._write_command('LISTENER')
self._write(chr(address & 0x1f))
def untalk(self):
self._write_command('UNTALK')
def unlisten(self):
self._write_command('UNLISTEN')
def lockout(self):
self._write_command('LOCKOUT')
def clear(self, bus=False):
flags = ['BOOLEAN'] if bus else []
self._write_command('CLEAR', flags=flags)
def unlock(self):
self._write_command('UNLOCK')
def trigger(self):
self._write_command('TRIGGER')
def read(self):
data = b''
# send initial read command
self._write_command('READ')
resp = self._read_command()
# strings are processed at once
if resp[0] == 'STRING':
data = self._read_line()
# chunked streams iteration
elif resp[0] == 'CHUNK':
while True:
size = ord(self._read())
data += self._read(size=size)
# transmission is over
if 'BOOLEAN' in resp[1]:
break
# ask for a new chunk
self._write_command('READ')
resp = self._read_command()
if resp[0] != 'CHUNK':
raise AgpibError('chunk stream interrupted by'
' command: %s' % resp[0])
else:
raise AgpibError('expected to get a data transfert but'
' received command: %s' % resp[0])
return data
def write(self, data):
for offset in range(0, len(data), 255):
size = min(255, len(data) - offset)
if size == 255:
self._write_command('WRITE')
else:
self._write_command('WRITE', flags=['BOOLEAN'])
self._write(chr(size))
self._write(data[offset : offset + size])
def output(self, address):
self.untalk()
self.unlisten()
self.talker(self._address)
self.listener(address)
def enter(self, address):
self.untalk()
self.unlisten()
self.talker(address)
self.listener(self._address)

117
arduino.py Normal file
View file

@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012,2013 Thibault VINCENT <tibal@reloaded.fr>
#
# This file is part of Agipibi.
#
# Agipibi is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Agipibi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Agipibi. If not, see <http://www.gnu.org/licenses/>.
#
import serial
class ArduinoError(Exception):
pass
class Arduino(object):
CMD_OUT = {}
CMD_IN = {}
FLAGS = {}
def __init__(self, device=None, debug=False):
if not device:
raise ArduinoError('No device specified')
self._arduino = serial.Serial(port=device, baudrate=115200)
self._debug = debug
def _read(self, size=1, timeout=None):
if timeout is not None:
self._arduino.timeout = timeout
received = self._arduino.read(size=size)
if self._debug:
print('R', ' '.join('{0:x}'.format(c) for c in received))
if timeout is not None:
self._arduino.timeout = None
if len(received) != size:
raise ArduinoError('received less bytes than expected'
' from Arduino: %d/%d' % (len(received), size))
return received
def _read_command(self, timeout=None):
byte = self._read(timeout=timeout)[0]
b_command = byte & 0x3f
b_flags = (byte & 0x60) >> 6
command = None
flags = []
for c_name, c_value in self.CMD_IN.items():
if b_command == c_value:
command = c_name
break
if command is None:
raise ArduinoError('received an unknown command from'
' Arduino: %x' % b_command)
flags_sum = 0
for f_name, f_value in self.FLAGS.items():
if b_flags & f_value:
flags.append(f_name)
flags_sum |= f_value
if b_flags != flags_sum:
raise ArduinoError('received an unknown flag bit: %x' % b_flags)
return (command, flags)
def _read_line(self, timeout=None):
line = b''
while True:
char = self._read(timeout=timeout)
if char == b'\n':
break
elif char != b'\r':
line += char
return line
def _write(self, data):
try:
if self._debug:
print('T', ' '.join('{0:x}'.format(ord(c)) for c in data))
written = self._arduino.write(data.encode('UTF-8'))
except serial.SerialTimeoutException as err:
raise ArduinoError('timeout while sending bytes: %s' % err)
if written != len(data):
raise ArduinoError('sent less bytes than expected to'
' Arduino: %d/%d' % (len(written), len(data)))
def _write_command(self, command, flags=None):
if command not in self.CMD_OUT:
raise ArduinoError('not sending invalid command: %s' % command)
flags_value = 0
if flags is not None:
for flag in flags:
if flag in self.FLAGS:
flags_value |= self.FLAGS[flag]
else:
raise ArduinoError('unknown flag: %s' % flag)
byte = ((self.CMD_OUT[command] & 0x3f) + (flags_value << 6)) & 0xff
self._write(chr(byte))

263
main.py Normal file
View file

@ -0,0 +1,263 @@
import sys
import math
import json
import random
import time
import numpy as np
from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QMessageBox
from PyQt5.QtGui import QPalette, QColor
from PyQt5.uic import loadUi
from PyQt5.QtChart import QChart, QLineSeries, QChartView
from agpib import Agpib
class Main(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self._ui = loadUi('main.ui')
self._ui.show()
# self.settings = QDialog()
# loadUi('./settings._ui', self.settings)
self._v_set = 0
self._i_set = 0
self._config = {}
self._gpib = None
self._chart = None
self.load_settings()
self.init_gpib()
self.init_chart()
def closeEvent(self, event):
self.save_settings()
self.deleteLater()
def init_gpib(self):
self._gpib = Agpib('/dev/ttyACM0')
if not self._gpib.interface_ping():
print('Failed to connect to gateway')
exit()
self._gpib.init()
self._gpib.remote(True)
self._gpib.output(3)
def init_chart(self):
self._chart = QChart()
self._chart.setTitle('Test')
self._ui.chart.setMouseTracking(True)
self._ui.chart.setInteractive(True)
self._ui.chart.setChart(self._chart)
self._ui.chart.setRubberBand(QChartView.RectangleRubberBand)
def _cmd(self, mode, v, i):
self._v_set = v
self._i_set = i
self._gpib.output(3)
self._gpib.write(f'M{mode} P{v}V C{i}A G')
def read_vm(self):
self._gpib.output(3)
self._gpib.write(f'T')
self._gpib.enter(3)
res = self._gpib.read()
res = res.decode('UTF-8')
status = res[0]
v_a = res[1]
val = float(res[2:7])
v = 0
a = 0
if v_a == 'V':
i = self._i_set
v = val
else:
i = val
v = self._v_set
return status, v, i
def perform_experiment(self):
v_series = QLineSeries()
i_series = QLineSeries()
v_series.setName('V')
i_series.setName('I')
self._chart.addSeries(v_series)
self._chart.addSeries(i_series)
self._chart.createDefaultAxes()
start_time = time.time()
v_axis_x = self._chart.axisX(v_series)
v_axis_y = self._chart.axisY(v_series)
i_axis_x = self._chart.axisX(i_series)
i_axis_y = self._chart.axisY(i_series)
v_minx = 1000
v_miny = 1000
v_maxx = -1000
v_maxy = -1000
i_minx = 1000
i_miny = 1000
i_maxx = -1000
i_maxy = -1000
v_avg = 0
i_avg = 0
print('Performing experiment')
cmds = np.linspace(self._ui.start.value(), self._ui.end.value(), self._ui.steps.value())
for cmd in cmds:
mode = None
if self._ui.mode.currentIndex() == 0:
mode = 1 # CV
else:
mode = 2 # CC
v_cmd = None
i_cmd = None
if self._ui.control_variable.currentIndex() == 0:
v_cmd = cmd
i_cmd = self._ui.other_variable.value()
else:
v_cmd = self._ui.other_variable.value()
i_cmd = cmd
self._cmd(mode, v_cmd, i_cmd)
time.sleep(self._ui.settle_time.value())
for _ in range(self._ui.samples.value()):
status, v, i = self.read_vm()
print(v, i)
x = time.time() - start_time
v_avg = (v_avg + v) / 2
i_avg = (i_avg + i) / 2
v_y = v_avg
i_y = i_avg
v_minx = min(v_minx, x)
v_maxx = max(v_maxx, x)
v_miny = min(v_miny, v_y)
v_maxy = max(v_maxy, v_y)
i_minx = min(i_minx, x)
i_maxx = max(i_maxx, x)
i_miny = min(i_miny, i_y)
i_maxy = max(i_maxy, i_y)
v_series.append(x, v_y)
i_series.append(x, i_y)
time.sleep(self._ui.sample_time.value())
print('Done experiment')
self._cmd(1, 0, 0)
v_axis_x.setMin(v_minx)
v_axis_x.setMax(v_maxx)
v_axis_y.setMin(v_miny)
v_axis_y.setMax(v_maxy)
i_axis_x.setMin(i_minx)
i_axis_x.setMax(i_maxx)
i_axis_y.setMin(i_miny)
i_axis_y.setMax(i_maxy)
def init_connections(self):
#############
# LED TAB #
#############
self._ui.perform.clicked.connect(self.perform_experiment)
# Light switch and mode
#self._ui.leds_enable.stateChanged.connect(self.enable_toggled)
#self._ui.leds_mode.currentIndexChanged.connect(self.leds.set_effect)
# RGB sliders
#self._ui.leds_r.sliderReleased.connect(self.color_changed)
#self._ui.leds_g.sliderReleased.connect(self.color_changed)
#self._ui.leds_b.sliderReleased.connect(self.color_changed)
#self._ui.leds_r_b.editingFinished.connect(self.color_changed)
#self._ui.leds_g_b.editingFinished.connect(self.color_changed)
#self._ui.leds_b_b.editingFinished.connect(self.color_changed)
# Brightness and speed
#self._ui.leds_brightness.sliderReleased.connect(self.brightness_changed)
#self._ui.leds_speed.sliderReleased.connect(self.speed_changed)
#####################
# SETTINGS DIALOG #
#####################
#self._ui.show_settings.clicked.connect(self.show_settings)
pass
#############
# LED TAB #
#############
def enable_toggled(self, state):
self.leds.set_effect(1 if state else 0)
def color_changed(self):
self.leds.set_effect(1)
self.leds.set_color(self._ui.leds_r.value(), self._ui.leds_g.value(), self._ui.leds_b.value())
def brightness_changed(self):
self.leds.set_brightness(self._ui.leds_brightness.value())
def speed_changed(self):
self.leds.set_speed(self._ui.leds_speed.value())
#############
# SETTINGS #
#############
def show_settings(self):
self.settings.show()
def save_settings(self):
with open('settings.json', 'w') as f:
self._config['mode'] = self._ui.mode.currentIndex()
self._config['control_variable'] = self._ui.control_variable.currentIndex()
self._config['other_variable'] = self._ui.other_variable.value()
self._config['start'] = self._ui.start.value()
self._config['end'] = self._ui.end.value()
self._config['steps'] = self._ui.steps.value()
self._config['settle_time'] = self._ui.settle_time.value()
self._config['samples'] = self._ui.samples.value()
self._config['sample_time'] = self._ui.sample_time.value()
json.dump(self._config, f)
def load_settings(self):
with open('settings.json', 'r') as f:
self._config = json.load(f)
self._ui.mode.setCurrentIndex(self._config['mode'])
self._ui.control_variable.setCurrentIndex(self._config['control_variable'])
self._ui.other_variable.setValue(self._config['other_variable'])
self._ui.start.setValue(self._config['start'])
self._ui.end.setValue(self._config['end'])
self._ui.steps.setValue(self._config['steps'])
self._ui.settle_time.setValue(self._config['settle_time'])
self._ui.samples.setValue(self._config['samples'])
self._ui.sample_time.setValue(self._config['sample_time'])
def main():
app = QApplication(sys.argv)
main = Main()
main.init_connections()
r = app.exec()
main.closeEvent(None)
sys.exit(r)
if __name__ == "__main__":
main()

199
main.ui Normal file
View file

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>879</width>
<height>617</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>GroupBox</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Mode</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="mode">
<item>
<property name="text">
<string>CV</string>
</property>
</item>
<item>
<property name="text">
<string>CC</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>ERROR</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="other_variable"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="end"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Steps</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="steps"/>
</item>
<item row="6" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Settle Time</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="settle_time"/>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Samples</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="samples"/>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Sample Time</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QDoubleSpinBox" name="sample_time"/>
</item>
<item row="10" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="11" column="0" colspan="2">
<widget class="QPushButton" name="perform">
<property name="text">
<string>Perform</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="start"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Variable</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="control_variable">
<item>
<property name="text">
<string>Voltage</string>
</property>
</item>
<item>
<property name="text">
<string>Current</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" rowspan="2">
<widget class="QChartView" name="chart" native="true"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>879</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>QChartView</class>
<extends>QWidget</extends>
<header>PyQt5.QtChart</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

1
settings.json Normal file
View file

@ -0,0 +1 @@
{"mode": 1, "control_variable": 1, "other_variable": 60.0, "start": 0.0, "end": 5.0, "steps": 10, "settle_time": 0.25, "samples": 10, "sample_time": 0.0}