Fuck
This commit is contained in:
commit
4f359e604a
12 changed files with 1051 additions and 0 deletions
4
.idea/encodings.xml
Normal file
4
.idea/encodings.xml
Normal 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
13
.idea/hit.iml
Normal 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
7
.idea/misc.xml
Normal 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
8
.idea/modules.xml
Normal 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
271
.idea/workspace.xml
Normal 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>
|
BIN
__pycache__/agpib.cpython-310.pyc
Normal file
BIN
__pycache__/agpib.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/arduino.cpython-310.pyc
Normal file
BIN
__pycache__/arduino.cpython-310.pyc
Normal file
Binary file not shown.
168
agpib.py
Normal file
168
agpib.py
Normal 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
117
arduino.py
Normal 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
263
main.py
Normal 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
199
main.ui
Normal 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
1
settings.json
Normal 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}
|
Loading…
Reference in a new issue