Repository: JorgeMaker/SimpleFOCStudio Branch: main Commit: 6fe2896155bc Files: 37 Total size: 164.2 KB Directory structure: gitextract_ef7r4v5m/ ├── .github/ │ └── workflows/ │ └── create_executables.yml ├── .gitignore ├── LICENSE ├── README.md ├── command_to_generate_spec.txt ├── device.json ├── requirements.txt ├── scriptTest.py ├── simpleFOCStudio.py ├── simpleFOCStudio.spec └── src/ ├── gui/ │ ├── commandlinetool/ │ │ ├── commandlinetool.py │ │ └── configureConnectionWidget.py │ ├── configtool/ │ │ ├── configureConnectionDialog.py │ │ ├── connectionControl.py │ │ ├── controlLoopConfig.py │ │ ├── deviceConfigurationTool.py │ │ ├── deviceInteractionFrame.py │ │ ├── deviceJoggingControl.py │ │ ├── deviceTreeview.py │ │ ├── devicesInspectorTree.py │ │ ├── droDisplayWidget.py │ │ ├── generalControls.py │ │ ├── generalSettingsWidget.py │ │ ├── generatedCodeDisplay.py │ │ ├── graphicWidget.py │ │ ├── pidConfiguration.py │ │ ├── torqueConfig.py │ │ └── treeViewConfigTool.py │ ├── mainWindow.py │ ├── resources/ │ │ ├── add_motor.psd │ │ ├── motor.psd │ │ └── studioicon.icns │ ├── sharedcomnponets/ │ │ ├── commandLineInterface.py │ │ └── sharedcomponets.py │ ├── toolbar.py │ └── workAreaTabbedWidget.py └── simpleFOCConnector.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/create_executables.yml ================================================ name: Package Application with Pyinstaller on: push: tags: - "v*.*.*" jobs: build_windows: runs-on: windows-latest steps: - name: Create Executable uses: sayyid5416/pyinstaller@v1 with: spec: 'simpleFOCStudio.spec' requirements: 'requirements.txt' python_ver: '3.9' python_arch: 'x64' exe_path: './dist/windows' - name: Rename file with details run: mv dist/windows/simpleFOCStudio.exe dist/windows/simpleFOCStudio-windows-x86_64-${{ github.ref_name }}.exe - name: Release uses: softprops/action-gh-release@v1 with: files: | dist/windows/** build_linux: runs-on: ubuntu-latest steps: - name: Install pyqt5 dependencies run: sudo apt install -y qtbase5-dev - name: Create Executable uses: sayyid5416/pyinstaller@v1 with: spec: 'simpleFOCStudio.spec' requirements: 'requirements.txt' python_ver: '3.9' python_arch: 'x64' exe_path: './dist/linux' - name: Rename file with details run: mv dist/linux/simpleFOCStudio dist/linux/simpleFOCStudio-linux-x86_64-${{ github.ref_name }} - name: Release uses: softprops/action-gh-release@v1 with: files: | dist/linux/** ================================================ FILE: .gitignore ================================================ /venv /.idea **/*.log **/__pycache__ build/ dist/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 JorgeMaker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ## *Simple**FOC**Studio* Graphical user interface for the [*Simple**FOC**library*](https://github.com/simplefoc). This application allows to tune and configure any BLDC/Stepper *Simple**FOC**library* controlled device, using serial port communications and the [Commander](https://docs.simplefoc.com/commander_interface) interface. #### The main features are: - Plug and play with the *Simple**FOC**library* version 2.1 - Real-time tuning and configuration of the motors - Real-time plotting and monitoring of motor variables - Code generation for easier integration of the tuned parameters in your code - Built on PyQt5 and a standardized `SimpleFOCConnector` interface that can be used as a gateway form python to the *Simple**FOC**library* device.

### Installation Don't worry, *Simple**FOC**Studio* is easy to install even if you have never used the terminal before! 😃 There are just couple of steps to take: 1. Install Python if you don't have it installed yet - We suggest to use Anaconda. [Here is how to install it.](https://docs.anaconda.com/anaconda/install/) - Once you have your Anaconda running open your terminal (on windows anaconda prompt) and run: ```sh conda create -n simplefoc python=3.9.0 ``` - Once this is done you will never have to run that command again, from now on you will just need: ```sh conda activate simplefoc ``` 2. Clone this repository or download the zip file 3. Enter the folder containing the repository using the terminal - the command will be something like this: ```sh cd some_path_on_disk/SimpleFOCStudio ``` 4. Final step of the installation is installing all the necessary libraries for the *Simple**FOC**Studio* : ```sh pip install -r "requirements.txt" ``` Once you have done all the steps above you do not need to repeat them any more. All you need to do the next time is open your terminal in the *Simple**FOC**Studio* directory and run the command: ```sh python simpleFOCStudio.py ``` Or if using Anaconda: ```sh conda activate simplefoc python simpleFOCStudio.py ``` ### Usage *Simple**FOC**Studio* has several useful features: - A simple approach to tuning your motor setup - Form view for fast motion control PID/LPF tuning - TreeView for more in depth tunning and experimenting - Code generation for transferring the found parameters into your arduino code - Serial terminal integrated with various commander features #### Motion control tunning windows Once you have your application running add a device by clicking the motor button in the toolbar. You can choose either the TreeView or the FormView. - To connect to your device first configure the serial port by clicking on Configure button - Add your com port info and click OK - Then add the device command ID that you've added to the commander usually its `M` - Command `M` , Arduino code : `command.add('M',doMotor,"my motor")` - Command `A` , Arduino code : `command.add('A',doMotor,"my motor")` - Then click to the Connect button and you should be ready to go!

#### Code generation *Simple**FOC**Studio* helps you to easier transfer your carefully tuned parameters to the Arduino code. Once you are happy with the performance of your system you can automatically generate the arduino code of the parameters you have tuned. To generate the code : - Click on the Arudino button in the toolbar. - Choose which sets of parameters you wish to generate the code for and click OK - In the new tab you will have a code of your tuned parameters. The generated code you can just copy/paste in your setup() function, just before calling the motor.init()

#### Custom Commands You can create your own custom commands if you [extend the Commnader interface](https://docs.simplefoc.com/commander_interface) in your sketch. This can be used for example to do things like change register settings for SPI devicesor any oyher functionality. Each custom command has a name and a value as you can see at the below image.

Once you have added each custom command in order to execute it you just need to select it and once selected press the space key (⎵) or right arrow key (→). #### Jogging panel

This panel is used to actuate with yor motor without having to write comands, is like using a joystick but pressing buttons. Each buton performas an action: - Fast Reverse button: reduces the current target by 2 times the specified increment. - Reverse button: reduces the current target by the specified increment. - Fordward button: Increase the current target by the specified increment. - Fast Fordward button: Increase the current target by 2 times the specified increment. - Stop button: in angle control mode, sets the current angle as target angle and in velocity control sets the target to zero. #### Integrated serial terminal *Simple**FOC**Studio* also has integrated serial terminal for easier debugging and monitoring.

### Arduino code Basically there are two things you need to do: 1. Use the commander interface and add the motor to the commander 2. Use the monitoring and add the `motor.monitor()` in the loop Here is a mockup of the code: ```cpp #include .... // include commander interface Commander command = Commander(Serial); void doMotor(char* cmd) { command.motor(&motor, cmd); } void setup(){ .... // add the motor to the commander interface // The letter (here 'M') you will provide to the SimpleFOCStudio command.add('M',doMotor,"motor"); // tell the motor to use the monitoring motor.useMonitoring(Serial); motor.monitor_downsample = 0; // disable monitor at first - optional ... } void loop(){ .... .... // real-time monitoring calls motor.monitor(); // real-time commander calls command.run(); } ``` ================================================ FILE: command_to_generate_spec.txt ================================================ pyi-makespec --onefile --noconsole --icon src/gui/resources/studioicon.icns --add-data src:src simpleFOCStudio.py ================================================ FILE: device.json ================================================ { "LPFAngle": 0.0, "LPFCurrentD": 0.005, "LPFCurrentQ": 0.005, "LPFVelocity": 0.2, "PIDAngle": { "D": 0.0, "I": 0.0, "P": 20.0, "outputLimit": 10.0, "outputRamp": 0.0 }, "PIDCurrentD": { "D": 0.0, "I": 300.0, "P": 3.0, "outputLimit": 12.0, "outputRamp": 0.0 }, "PIDCurrentQ": { "D": 0.0, "I": 300.0, "P": 3.0, "outputLimit": 12.0, "outputRamp": 0.0 }, "PIDVelocity": { "D": 0.0, "I": 20.0, "P": 0.2, "outputLimit": 11.0, "outputRamp": 1000.0 }, "connectionID": "", "controlType": 2, "currentLimit": 0.2, "customCommands": [ { "commandName": "Command1", "commandValue": "D100" } ], "devCommandID": "M", "initialTarget": 0, "motionDownsample": 0.0, "phaseResistance": 0.0, "sensorElectricalZero": 2.676, "sensorZeroOffset": 0.0, "serialByteSize": 8, "serialParity": "N", "serialPortName": "/dev/cu.usbserial-14140", "serialRate": "115200", "stopBits": 1, "torqueType": 0, "velocityLimit": 10.0, "voltageLimit": 11.0 } ================================================ FILE: requirements.txt ================================================ # Requirements for SimpleFOCStudio PyQt5==5.15.9 pyqtgraph==0.13.1 pyserial==3.5 numpy==1.24.0 # Requirements for Pyinstaller packaging process: pyinstaller Pillow ================================================ FILE: scriptTest.py ================================================ from src.simpleFOCConnector import SimpleFOCDevice import time if __name__ == '__main__': deviceConnector = SimpleFOCDevice.getInstance() deviceConnector.serialPortName = 'COM32' deviceConnector.serialRate = 115200 deviceConnector.devCommandID = 'M' if deviceConnector.connect(SimpleFOCDevice.ONLY_CONNECT): while True: deviceConnector.sendControlType(SimpleFOCDevice.ANGLE_CONTROL) deviceConnector.sendTargetValue(0) time.sleep(2.0) deviceConnector.sendTargetValue(3) time.sleep(2) deviceConnector.sendTargetValue(6) time.sleep(2) deviceConnector.sendTargetValue(-6) time.sleep(2) deviceConnector.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL) deviceConnector.sendTargetValue(30) time.sleep(2) deviceConnector.sendTargetValue(-30) time.sleep(2) ================================================ FILE: simpleFOCStudio.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ This module contains ans script to start the SimpleFOC ConfigTool, a GIU application ta monitor, tune and configure BLDC motor controllers based on SimpleFOC library. """ from PyQt5 import QtWidgets, QtCore from src.gui.mainWindow import UserInteractionMainWindow import sys import logging if __name__ == '__main__': try: QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) logging.basicConfig(filename='.SimpleFOCConfigTool.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s') app = QtWidgets.QApplication(sys.argv) mainWindow = QtWidgets.QMainWindow() userInteractionMainWindow = UserInteractionMainWindow() userInteractionMainWindow.setupUi(mainWindow) mainWindow.show() sys.exit(app.exec_()) except Exception as exception:\ logging.error(exception, exc_info=True) ================================================ FILE: simpleFOCStudio.spec ================================================ # -*- mode: python ; coding: utf-8 -*- a = Analysis( ['simpleFOCStudio.py'], pathex=[], binaries=[], datas=[('src', 'src')], hiddenimports=[], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], noarchive=False, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name='simpleFOCStudio', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=False, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon=['src\\gui\\resources\\studioicon.icns'], ) ================================================ FILE: src/gui/commandlinetool/commandlinetool.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.gui.commandlinetool.configureConnectionWidget import \ ConfigureConnection from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget, GUIToolKit) from src.simpleFOCConnector import SimpleFOCDevice class CommandLineConsoleTool(WorkAreaTabWidget): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setObjectName('verticalLayout') self.configureConnection = ConfigureConnection() self.verticalLayout.addWidget(self.configureConnection) self.commandLineInterface = CommandLineWidget() self.verticalLayout.addWidget(self.commandLineInterface) self.device.commProvider.rawDataReceived.connect(self.commandLineInterface.publishCommandResponseData) def getTabIcon(self): return GUIToolKit.getIconByName('consoletool') def getTabName(self): return self.device.connectionID ================================================ FILE: src/gui/commandlinetool/configureConnectionWidget.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import serial from PyQt5 import (QtGui, QtCore, QtWidgets) from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.gui.sharedcomnponets.sharedcomponets import SerialPortComboBox from src.simpleFOCConnector import SimpleFOCDevice class ConfigureConnection(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setTitle('Configure serial connection') self.setObjectName('configureConnection') self.configCoonLayout = QtWidgets.QHBoxLayout(self) self.configCoonLayout.setObjectName( 'configureConnectionorizontalLayout') self.portNameLabel = QtWidgets.QLabel(self) self.portNameLabel.setObjectName('portNameLabel') self.configCoonLayout.addWidget(self.portNameLabel) self.portNameComboBox = SerialPortComboBox(self) self.portNameComboBox.setObjectName('portNameComboBox') self.portNameComboBox.setMinimumWidth(250) self.configCoonLayout.addWidget(self.portNameComboBox) self.bitRateLabel = QtWidgets.QLabel(self) self.bitRateLabel.setObjectName('bitRateLabel') self.configCoonLayout.addWidget(self.bitRateLabel) self.bitRatelineEdit = QtWidgets.QLineEdit(self) self.bitRatelineEdit.setObjectName('bitRatelineEdit') self.bitRatelineEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp("^[0-9]*$"))) self.bitRatelineEdit.setText('115200') self.configCoonLayout.addWidget(self.bitRatelineEdit) self.parityLabel = QtWidgets.QLabel(self) self.parityLabel.setObjectName('parityLabel') self.configCoonLayout.addWidget(self.parityLabel) self.parityComboBox = QtWidgets.QComboBox(self) self.parityComboBox.setObjectName('parityComboBox') self.parityComboBox.addItems(serial.PARITY_NAMES.values()) self.configCoonLayout.addWidget(self.parityComboBox) serial.PARITY_NAMES.values() self.byteSizeLabel = QtWidgets.QLabel(self) self.byteSizeLabel.setObjectName('byteSizeLabel') self.configCoonLayout.addWidget(self.byteSizeLabel) self.byteSizeComboBox = QtWidgets.QComboBox(self) self.byteSizeComboBox.setObjectName('byteSizeComboBox') byteSizeList = [str(serial.EIGHTBITS), str(serial.FIVEBITS), str(serial.SIXBITS), str(serial.SEVENBITS)] self.byteSizeComboBox.addItems(byteSizeList) self.configCoonLayout.addWidget(self.byteSizeComboBox) self.stopBitsLabel = QtWidgets.QLabel(self) self.stopBitsLabel.setObjectName('stopBitsLabel') self.configCoonLayout.addWidget(self.stopBitsLabel) self.stopBitsComboBox = QtWidgets.QComboBox(self) byteStopBitsList = [str(serial.STOPBITS_ONE), str(serial.STOPBITS_ONE_POINT_FIVE), str(serial.STOPBITS_TWO)] self.stopBitsComboBox.addItems(byteStopBitsList) self.stopBitsComboBox.setObjectName('stopBitsComboBox') self.configCoonLayout.addWidget(self.stopBitsComboBox) self.connectDisconnectButton = QtWidgets.QPushButton(self) self.connectDisconnectButton.setIcon( GUIToolKit.getIconByName('connect')) self.connectDisconnectButton.setObjectName('connectDeviceButton') self.connectDisconnectButton.setText('Connect') self.connectDisconnectButton.clicked.connect( self.connectDisconnectDeviceAction) self.configCoonLayout.addWidget(self.connectDisconnectButton) self.portNameLabel.setText('Port Name') self.bitRateLabel.setText('Bit rate') self.parityLabel.setText('Parity') self.byteSizeLabel.setText('Byte size') self.stopBitsLabel.setText('Stop bits') self.device.addConnectionStateListener(self) self.connectionStateChanged(self.device.isConnected) def getConfigValues(self): values = { 'connectionID': '', 'serialPortName': self.portNameComboBox.currentText(), 'serialRate': self.bitRatelineEdit.text(), 'stopBits': self.stopBitsExtractor(self.stopBitsComboBox.currentText()), 'serialByteSize': int(str(self.byteSizeComboBox.currentText())), 'serialParity': list(serial.PARITY_NAMES.keys())[list(serial.PARITY_NAMES.values()).index(self.parityComboBox.currentText())][0] } return values def stopBitsExtractor(self, value): if value == '1.5': return float(self.stopBitsComboBox.currentText()) else: return int(self.stopBitsComboBox.currentText()) def connectionStateChanged(self, isConnectedFlag): if isConnectedFlag: self.connectDisconnectButton.setText('Disconnect') self.connectDisconnectButton.setIcon( GUIToolKit.getIconByName('disconnect')) else: self.connectDisconnectButton.setText('Connect') self.connectDisconnectButton.setIcon( GUIToolKit.getIconByName('connect')) self.portNameLabel.setEnabled(not isConnectedFlag) self.portNameComboBox.setEnabled(not isConnectedFlag) self.bitRateLabel.setEnabled(not isConnectedFlag) self.bitRatelineEdit.setEnabled(not isConnectedFlag) self.parityLabel.setEnabled(not isConnectedFlag) self.parityComboBox.setEnabled(not isConnectedFlag) self.byteSizeLabel.setEnabled(not isConnectedFlag) self.byteSizeComboBox.setEnabled(not isConnectedFlag) self.stopBitsLabel.setEnabled(not isConnectedFlag) self.stopBitsComboBox.setEnabled(not isConnectedFlag) def connectDisconnectDeviceAction(self): if self.device.isConnected: self.disConnectAction() else: self.connectAction() def connectAction(self): deviceConfig = self.getConfigValues() self.device.configureConnection(deviceConfig) self.device.connect(SimpleFOCDevice.ONLY_CONNECT) def disConnectAction(self): self.device.disConnect() ================================================ FILE: src/gui/configtool/configureConnectionDialog.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import serial from PyQt5 import (QtCore, QtGui, QtWidgets) from src.gui.sharedcomnponets.sharedcomponets import SerialPortComboBox from src.simpleFOCConnector import SimpleFOCDevice class ConfigureSerailConnectionDialog(QtWidgets.QDialog): def __init__(self, parent=None): super().__init__(parent) self.setupUi(SimpleFOCDevice.getInstance()) def setupUi(self, device=None): self.setObjectName('Dialog') self.resize(700, 188) self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.setObjectName('gridLayout') self.portNameLabel = QtWidgets.QLabel(self) self.portNameLabel.setObjectName('portNameLabel') self.gridLayout.addWidget(self.portNameLabel, 0, 0, 1, 1) self.portNameComboBox = SerialPortComboBox(self) self.portNameComboBox.setObjectName('portNameComboBox') self.portNameComboBox.setMinimumWidth(250) self.gridLayout.addWidget(self.portNameComboBox, 0, 1, 1, 1) self.bitRateLabel = QtWidgets.QLabel(self) self.bitRateLabel.setObjectName('bitRateLabel') self.gridLayout.addWidget(self.bitRateLabel, 0, 2, 1, 1) self.bitRatelineEdit = QtWidgets.QLineEdit(self) self.bitRatelineEdit.setObjectName('bitRatelineEdit') self.bitRatelineEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp("^[0-9]*$"))) self.gridLayout.addWidget(self.bitRatelineEdit, 0, 3, 1, 1) self.parityLabel = QtWidgets.QLabel(self) self.parityLabel.setObjectName('parityLabel') self.gridLayout.addWidget(self.parityLabel, 1, 0, 1, 1) self.parityComboBox = QtWidgets.QComboBox(self) self.parityComboBox.setObjectName('parityComboBox') self.parityComboBox.addItems(serial.PARITY_NAMES.values()) self.gridLayout.addWidget(self.parityComboBox, 1, 1, 1, 1) serial.PARITY_NAMES.values() self.byteSizeLabel = QtWidgets.QLabel(self) self.byteSizeLabel.setObjectName('byteSizeLabel') self.gridLayout.addWidget(self.byteSizeLabel, 1, 2, 1, 1) self.byteSizeComboBox = QtWidgets.QComboBox(self) self.byteSizeComboBox.setObjectName('byteSizeComboBox') byteSizeList = [str(serial.EIGHTBITS), str(serial.FIVEBITS), str(serial.SIXBITS), str(serial.SEVENBITS)] self.byteSizeComboBox.addItems(byteSizeList) self.gridLayout.addWidget(self.byteSizeComboBox, 1, 3, 1, 1) self.stopBitsLabel = QtWidgets.QLabel(self) self.stopBitsLabel.setObjectName('stopBitsLabel') self.gridLayout.addWidget(self.stopBitsLabel, 2, 0, 1, 1) self.stopBitsComboBox = QtWidgets.QComboBox(self) byteStopBitsList = [str(serial.STOPBITS_ONE), str(serial.STOPBITS_ONE_POINT_FIVE), str(serial.STOPBITS_TWO)] self.stopBitsComboBox.addItems(byteStopBitsList) self.stopBitsComboBox.setObjectName('stopBitsComboBox') self.gridLayout.addWidget(self.stopBitsComboBox, 2, 1, 1, 1) self.connectionIDLabel = QtWidgets.QLabel(self) self.connectionIDLabel.setObjectName('connectionNameLabel') self.gridLayout.addWidget(self.connectionIDLabel, 2, 2, 1, 1) self.connectionIDlineEdit = QtWidgets.QLineEdit(self) self.connectionIDlineEdit.setMaxLength(10) self.connectionIDlineEdit.setObjectName('connectionNameEdit') self.gridLayout.addWidget(self.connectionIDlineEdit, 2, 3, 1, 1) self.buttonBox = QtWidgets.QDialogButtonBox(self) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons( QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName('buttonBox') self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 4) self.setWindowTitle('Configure serial connection') self.portNameLabel.setText('Port Name') self.bitRateLabel.setText('Bit rate') self.parityLabel.setText('Parity') self.byteSizeLabel.setText('Byte size') self.stopBitsLabel.setText('Stop bits') self.connectionIDLabel.setText('Conn ID') self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) QtCore.QMetaObject.connectSlotsByName(self) if device is not None: self.fillForm(device) def fillForm(self, deviceConnector): self.connectionIDlineEdit.setText(deviceConnector.connectionID) self.portNameComboBox.setCurrentText(deviceConnector.serialPortName) self.bitRatelineEdit.setText(str(deviceConnector.serialRate)) self.stopBitsComboBox.setCurrentText(str(deviceConnector.stopBits)) self.byteSizeComboBox.setCurrentText(str(deviceConnector.serialByteSize)) self.parityComboBox.setCurrentText(str(deviceConnector.serialParity)) def getConfigValues(self): values = { 'connectionID': self.connectionIDlineEdit.text(), 'serialPortName': self.portNameComboBox.currentText(), 'serialRate': self.bitRatelineEdit.text(), 'stopBits': self.stopBitsExtractor(self.stopBitsComboBox.currentText()), 'serialByteSize': int(str(self.byteSizeComboBox.currentText())), 'serialParity': list(serial.PARITY_NAMES.keys())[list(serial.PARITY_NAMES.values()).index(self.parityComboBox.currentText())][0] } return values def stopBitsExtractor(self, value): if value == '1.5': return float(self.stopBitsComboBox.currentText()) else: return int(self.stopBitsComboBox.currentText()) ================================================ FILE: src/gui/configtool/connectionControl.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.gui.configtool.configureConnectionDialog import \ ConfigureSerailConnectionDialog from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class ConnectionControlGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('connectionControl') self.setTitle('Connection control') self.horizontalLayout = QtWidgets.QHBoxLayout(self) self.horizontalLayout.setObjectName('generalControlHL') self.devCommandIDLabel = QtWidgets.QLabel("Command:") self.horizontalLayout.addWidget(self.devCommandIDLabel) self.devCommandIDLetter = QtWidgets.QLineEdit() self.devCommandIDLetter.setObjectName('devCommandIDLetter') self.devCommandIDLetter.editingFinished.connect(self.changeDevicedevCommandID) self.horizontalLayout.addWidget(self.devCommandIDLetter) self.devCommandIDLetter.setText(self.device.devCommandID) self.pullConfig = QtWidgets.QPushButton() self.pullConfig.setObjectName('pullConfig') self.pullConfig.setIcon(GUIToolKit.getIconByName('pull')) self.pullConfig.setText(' Pull Params') self.pullConfig.clicked.connect(self.device.pullConfiguration) self.horizontalLayout.addWidget(self.pullConfig) self.connectDisconnectButton = QtWidgets.QPushButton(self) self.connectDisconnectButton.setIcon(GUIToolKit.getIconByName('connect')) self.connectDisconnectButton.setObjectName('connectDeviceButton') self.connectDisconnectButton.setText('Connect') self.connectDisconnectButton.clicked.connect(self.connectDisconnectDeviceAction) self.horizontalLayout.addWidget(self.connectDisconnectButton) self.configureDeviceButton = QtWidgets.QPushButton(self) self.configureDeviceButton.setIcon(GUIToolKit.getIconByName('configure')) self.configureDeviceButton.setObjectName('configureDeviceButton') self.configureDeviceButton.setText('Configure') self.configureDeviceButton.clicked.connect(self.configureDeviceAction) self.horizontalLayout.addWidget(self.configureDeviceButton) self.device.addConnectionStateListener(self) self.connectionStateChanged(self.device.isConnected) def changeDevicedevCommandID(self): self.device.devCommandID = self.devCommandIDLetter.text() def connectDisconnectDeviceAction(self): if self.device.isConnected: self.device.disConnect() else: connectionMode = SimpleFOCDevice.PULL_CONFIG_ON_CONNECT self.device.connect(connectionMode) def connectionStateChanged(self, isConnected): if isConnected: self.connectDisconnectButton.setIcon( GUIToolKit.getIconByName('disconnect')) self.connectDisconnectButton.setText('Disconnect') else: self.connectDisconnectButton.setIcon( GUIToolKit.getIconByName('connect')) self.connectDisconnectButton.setText('Connect') def configureDeviceAction(self): dialog = ConfigureSerailConnectionDialog() result = dialog.exec_() if result: deviceConfig = dialog.getConfigValues() self.device.configureConnection(deviceConfig) ================================================ FILE: src/gui/configtool/controlLoopConfig.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.simpleFOCConnector import SimpleFOCDevice class ControlLoopGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('controlLoop') self.setTitle('Control Loop Mode') self.controlLoopHorizontalLayout = QtWidgets.QHBoxLayout(self) self.controlLoopHorizontalLayout.setObjectName('controlLoopHorizontalLayout') self.selectorControlLoop = QtWidgets.QComboBox(self) self.selectorControlLoop.setObjectName('selectorControlLoop') self.selectorControlLoop.addItems(['Torque', 'Velocity', 'Angle', 'Velocity openloop', 'Angle openloop']) self.selectorControlLoop.currentIndexChanged.connect(self.changeControlLoop) self.controlLoopHorizontalLayout.addWidget(self.selectorControlLoop) self.setControlLopMode(self.device.controlType) self.disableUI() self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect( self.commandResponseReceived) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def setControlLopMode(self, value): if value == SimpleFOCDevice.TORQUE_CONTROL: self.selectorControlLoop.setCurrentIndex(0) elif value == SimpleFOCDevice.VELOCITY_CONTROL: self.selectorControlLoop.setCurrentIndex(1) elif value == SimpleFOCDevice.ANGLE_CONTROL: self.selectorControlLoop.setCurrentIndex(2) elif value == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL: self.selectorControlLoop.setCurrentIndex(3) elif value == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL: self.selectorControlLoop.setCurrentIndex(4) def changeControlLoop(self): index = self.selectorControlLoop.currentIndex() if index == 0: self.device.sendControlType(SimpleFOCDevice.TORQUE_CONTROL) elif index == 1: self.device.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL) elif index == 2: self.device.sendControlType(SimpleFOCDevice.ANGLE_CONTROL) elif index == 3: self.device.sendControlType(SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL) elif index == 4: self.device.sendControlType(SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL) def commandResponseReceived(self, cmdRespose): self.setControlLopMode(self.device.controlType) ================================================ FILE: src/gui/configtool/deviceConfigurationTool.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.gui.configtool.connectionControl import ConnectionControlGroupBox from src.gui.configtool.controlLoopConfig import ControlLoopGroupBox from src.gui.configtool.deviceJoggingControl import DeviceJoggingControl from src.gui.configtool.droDisplayWidget import DROGroupBox from src.gui.configtool.generalControls import GeneralControls from src.gui.configtool.generalSettingsWidget import GeneralSettingsGroupBox from src.gui.configtool.graphicWidget import SimpleFOCGraphicWidget from src.gui.configtool.pidConfiguration import PidGroupBox from src.gui.configtool.torqueConfig import TorqueGroupBox from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget, GUIToolKit) from src.simpleFOCConnector import SimpleFOCDevice class DeviceConfigurationTool(WorkAreaTabWidget): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('DeviceConfigurationTool') self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setObjectName('verticalLayout') self.counterWidget = QtWidgets.QWidget(self) self.counterWidget.setObjectName('counterWidget') self.horizontalLayout = QtWidgets.QHBoxLayout(self.counterWidget) self.horizontalLayout.setObjectName('horizontalLayout') self.digitalReadOut = DROGroupBox(self.counterWidget) self.horizontalLayout.addWidget(self.digitalReadOut) self.controlLoop = ControlLoopGroupBox(self.counterWidget) self.horizontalLayout.addWidget(self.controlLoop) self.torqueConfig = TorqueGroupBox(self.counterWidget) self.horizontalLayout.addWidget(self.torqueConfig) self.connectionControl = ConnectionControlGroupBox(self.counterWidget) self.horizontalLayout.addWidget(self.connectionControl) self.verticalLayout.addWidget(self.counterWidget) self.graphicWidget = SimpleFOCGraphicWidget() self.verticalLayout.addWidget(self.graphicWidget) self.bottomWidget = QtWidgets.QWidget(self) self.bottomWidget.setObjectName('bottomWidget') self.bottomHorizontalLayout = QtWidgets.QHBoxLayout(self.bottomWidget) self.bottomHorizontalLayout.setObjectName('configureHorizontalLayout') self.pidConfigurator = PidGroupBox(self.bottomWidget) self.bottomHorizontalLayout.addWidget(self.pidConfigurator) self.generalLayout = QtWidgets.QVBoxLayout() self.generalDeviceSettings = GeneralSettingsGroupBox(self.bottomWidget) self.generalControls = GeneralControls(self.bottomWidget) self.generalLayout.addWidget(self.generalControls) self.generalLayout.addWidget(self.generalDeviceSettings) self.bottomHorizontalLayout.addLayout(self.generalLayout) self.lasWidget = QtWidgets.QWidget(self) self.lastVerticalLayout = QtWidgets.QVBoxLayout(self.lasWidget) self.commandLine = CommandLineWidget(self) self.lastVerticalLayout.addWidget(self.commandLine) self.joggingControl = DeviceJoggingControl(self) self.lastVerticalLayout.addWidget(self.joggingControl) self.bottomHorizontalLayout.addWidget(self.lasWidget) self.verticalLayout.addWidget(self.bottomWidget) self.device.commProvider.commandDataReceived.connect(self.commandLine.publishCommandResponseData) def getTabIcon(self): return GUIToolKit.getIconByName('motor') def getTabName(self): return self.device.connectionID def configureConnection(self, configvalues): self.device.serialPortName = configvalues['serialPortName'] self.device.serialRate = configvalues['serialRate'] self.device.stopBits = configvalues['stopBits'] self.device.serialByteSize = configvalues['serialByteSize'] self.device.serialParity = configvalues['serialParity'] ================================================ FILE: src/gui/configtool/deviceInteractionFrame.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QVBoxLayout, QFrame, QSplitter) from src.gui.configtool.graphicWidget import SimpleFOCGraphicWidget from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget from src.simpleFOCConnector import SimpleFOCDevice class DeviceInteractionFrame(QFrame): def __init__(self, parent=None): super().__init__(parent) self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.device = SimpleFOCDevice.getInstance() self.graphicWidget = SimpleFOCGraphicWidget(self) self.cmdLineTollWidget = CommandLineWidget(self) self.cmdLineTollWidget.setMaximumHeight(150) self.verticalSplitter = QSplitter(Qt.Vertical) self.verticalSplitter.addWidget(self.graphicWidget) self.verticalSplitter.addWidget(self.cmdLineTollWidget) self.device.commProvider.commandDataReceived.connect( self.cmdLineTollWidget.publishCommandResponseData) self.layout.addWidget(self.verticalSplitter) self.setLayout(self.layout) ================================================ FILE: src/gui/configtool/deviceJoggingControl.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui, QtWidgets, QtCore) from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class DeviceJoggingControl(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('joggingControl') self.setTitle('Jogging control') self.horizontalLayout = QtWidgets.QHBoxLayout(self) self.horizontalLayout.setObjectName('generalControlHL') self.fastFordwardButton = QtWidgets.QPushButton() self.fastFordwardButton.setObjectName('fastbackward') self.fastFordwardButton.setIcon(GUIToolKit.getIconByName('fastbackward')) self.fastFordwardButton.clicked.connect(self.joggingFastBackward) self.horizontalLayout.addWidget(self.fastFordwardButton) self.backwardButton = QtWidgets.QPushButton() self.backwardButton.setObjectName('backward') self.backwardButton.setIcon(GUIToolKit.getIconByName('backward')) self.backwardButton.clicked.connect(self.joggingBackward) self.horizontalLayout.addWidget(self.backwardButton) self.stopButton = QtWidgets.QPushButton() self.stopButton.setObjectName('stopbutton') self.stopButton.setIcon(GUIToolKit.getIconByName('stopjogging')) self.stopButton.clicked.connect(self.joggingStop) self.horizontalLayout.addWidget(self.stopButton) self.fordwardButton = QtWidgets.QPushButton() self.fordwardButton.setObjectName('fordward') self.fordwardButton.setIcon(GUIToolKit.getIconByName('fordward')) self.fordwardButton.clicked.connect(self.joggingFordward) self.horizontalLayout.addWidget(self.fordwardButton) self.fastBackwardButton = QtWidgets.QPushButton() self.fastBackwardButton.setObjectName('fastfordward') self.fastBackwardButton.setIcon(GUIToolKit.getIconByName('fastfordward')) self.fastBackwardButton.clicked.connect(self.joggingfastFordward) self.horizontalLayout.addWidget(self.fastBackwardButton) self.incrementLabel = QtWidgets.QLabel("Increment:") self.horizontalLayout.addWidget(self.incrementLabel) onlyFloat = QtGui.QRegExpValidator( QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+")) self.incrementEdit = QtWidgets.QLineEdit() self.incrementEdit.setValidator(onlyFloat) self.incrementEdit.setAlignment(QtCore.Qt.AlignCenter) self.incrementEdit.setText('1.0') self.incrementEdit.setObjectName('incrementEdit') self.horizontalLayout.addWidget(self.incrementEdit) self.disableUI() self.device.addConnectionStateListener(self) def connectionStateChanged(self, isConnectedFlag): if isConnectedFlag is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def joggingFastBackward(self): currenttarget = self.device.targetNow increment = self.incrementEdit.text() newTarget = float(currenttarget) - 2 * float(increment) self.device.sendTargetValue(str(newTarget)) def joggingBackward(self): increment = self.incrementEdit.text() currenttarget = self.device.targetNow newTarget = float(currenttarget) - float(increment) self.device.sendTargetValue(str(newTarget)) def joggingStop(self): controltType = self.device.controlType if (controltType == SimpleFOCDevice.ANGLE_CONTROL or controltType == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL): self.device.sendTargetValue(self.device.angleNow) if (controltType == SimpleFOCDevice.VELOCITY_CONTROL or controltType == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL): self.device.sendTargetValue('0') def joggingFordward(self): increment = self.incrementEdit.text() currenttarget = self.device.targetNow newTarget = float(currenttarget) + float(increment) self.device.sendTargetValue(str(newTarget)) def joggingfastFordward(self): increment = self.incrementEdit.text() currenttarget = self.device.targetNow newTarget = float(currenttarget) + 2 * float(increment) self.device.sendTargetValue(str(newTarget)) ================================================ FILE: src/gui/configtool/deviceTreeview.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtWidgets,QtCore) from PyQt5.Qt import QTreeWidget from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice from src.simpleFOCConnector import Command class DeviceTreeView(QTreeWidget): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.sFOCDevice = QtWidgets.QTreeWidgetItem(self) self.sFOCDevice.setText(0, 'sFOC Device') self.sFOCDevice.setIcon(0, GUIToolKit.getIconByName('motor')) self.setColumnCount(2) self.motionControl = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.motionControl.setText(0, 'Motion config') self.motionControl.setIcon(0, GUIToolKit.getIconByName('pidconfig')) self.sFOCDevice.addChild(self.motionControl) self.controller = QtWidgets.QTreeWidgetItem(self.motionControl) self.controller.setText(0, 'Motion Control Type') self.controller.setIcon(0, GUIToolKit.getIconByName('gear')) self.selectorControlLoop = QtWidgets.QComboBox(self) self.selectorControlLoop.addItems(['Torque', 'Velocity', 'Angle', 'Velocity openloop', 'Angle openloop']) self.selectorControlLoop.currentIndexChanged.connect(self.changeControlLoop) self.setItemWidget(self.controller,1,self.selectorControlLoop) self.torque = QtWidgets.QTreeWidgetItem(self.motionControl) self.torque.setText(0, 'Torque Control Type') self.torque.setIcon(0, GUIToolKit.getIconByName('gear')) self.selectorTorque = QtWidgets.QComboBox(self) self.selectorTorque.addItems(['Voltage', 'DC Curret', 'FOC Current']) self.selectorTorque.currentIndexChanged.connect(self.changeTorque) self.setItemWidget(self.torque,1,self.selectorTorque) self.motionDownsample = QtWidgets.QTreeWidgetItem(self.motionControl) self.motionDownsample.setText(0, 'Motion Downsample') self.motionDownsample.setIcon(0, GUIToolKit.getIconByName('gear')) self.motionDownsample.setText(1, '') self.motionDownsample.setFlags( self.motionDownsample.flags() | QtCore.Qt.ItemIsEditable) self.PIDVelocityConfig = self.addPIDSubtree(self.motionControl,'Velocity PID') self.PIDAngleConfig = self.addPIDSubtree(self.motionControl,'Angle PID') self.PIDCurrentQConfig = self.addPIDSubtree(self.motionControl,'Current q PID') self.PIDCurrentDConfig = self.addPIDSubtree(self.motionControl,'Current d PID') self.limitsConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.limitsConfig.setText(0, 'Limits') self.limitsConfig.setIcon(0, GUIToolKit.getIconByName('statistics')) self.sFOCDevice.addChild(self.limitsConfig) self.velocityLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig) self.velocityLimit.setText(0, 'Velocity limit') self.velocityLimit.setIcon(0, GUIToolKit.getIconByName('gear')) self.velocityLimit.setText(1, '') self.velocityLimit.setFlags( self.velocityLimit.flags() | QtCore.Qt.ItemIsEditable) self.voltageLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig) self.voltageLimit.setText(0, 'Voltage limit') self.voltageLimit.setIcon(0, GUIToolKit.getIconByName('gear')) self.voltageLimit.setText(1, '') self.voltageLimit.setFlags( self.voltageLimit.flags() | QtCore.Qt.ItemIsEditable) self.currentLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig) self.currentLimit.setText(0, 'Current limit') self.currentLimit.setIcon(0, GUIToolKit.getIconByName('gear')) self.currentLimit.setText(1, '') self.currentLimit.setFlags( self.currentLimit.flags() | QtCore.Qt.ItemIsEditable) self.statesConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.statesConfig.setText(0, 'States') self.statesConfig.setIcon(0, GUIToolKit.getIconByName('statistics')) self.sFOCDevice.addChild(self.statesConfig) self.satateTarget = QtWidgets.QTreeWidgetItem(self.statesConfig) self.satateTarget.setText(0, 'Target') self.satateTarget.setIcon(0, GUIToolKit.getIconByName('gear')) self.satateTarget.setText(1, '') self.stateVq = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateVq.setText(0, 'Voltage q') self.stateVq.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateVd = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateVd.setText(0, 'Voltage d') self.stateVd.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateCq = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateCq.setText(0, 'Current q') self.stateCq.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateCd = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateCd.setText(0, 'Current d') self.stateCd.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateVel = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateVel.setText(0, 'Velocity') self.stateVel.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateVel.setText(1, '') self.stateAngle = QtWidgets.QTreeWidgetItem(self.statesConfig) self.stateAngle.setText(0, 'Angle') self.stateAngle.setIcon(0, GUIToolKit.getIconByName('gear')) self.stateAngle.setText(1, '') self.sensorConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.sensorConfig.setText(0, 'Sensor config') self.sensorConfig.setIcon(0, GUIToolKit.getIconByName('sensor')) self.sFOCDevice.addChild(self.sensorConfig) self.sensorZeroOffset = QtWidgets.QTreeWidgetItem(self.sensorConfig) self.sensorZeroOffset.setText(0, 'Zero Angle Offset') self.sensorZeroOffset.setIcon(0, GUIToolKit.getIconByName('gear')) self.sensorZeroOffset.setText(1, '') self.sensorZeroOffset.setFlags( self.sensorZeroOffset.flags() | QtCore.Qt.ItemIsEditable) self.sensorZeroElecOffset = QtWidgets.QTreeWidgetItem(self.sensorConfig) self.sensorZeroElecOffset.setText(0, 'Electrical Zero Offset') self.sensorZeroElecOffset.setIcon(0, GUIToolKit.getIconByName('gear')) self.sensorZeroElecOffset.setText(1, '') self.sensorZeroElecOffset.setFlags( self.sensorZeroElecOffset.flags() | QtCore.Qt.ItemIsEditable) self.generalSettings = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.generalSettings.setText(0, 'General settings') self.generalSettings.setIcon(0, GUIToolKit.getIconByName('generalsettings')) self.sFOCDevice.addChild(self.generalSettings) self.phaseRes = QtWidgets.QTreeWidgetItem(self.generalSettings) self.phaseRes.setText(0, 'Phase Resistance') self.phaseRes.setIcon(0, GUIToolKit.getIconByName('res')) self.phaseRes.setText(1, '') self.phaseRes.setFlags( self.phaseRes.flags() | QtCore.Qt.ItemIsEditable) self.deviceStatus = QtWidgets.QTreeWidgetItem(self.generalSettings) self.deviceStatus.setText(0, 'Motor Status') self.deviceStatus.setIcon(0, GUIToolKit.getIconByName('gear')) self.selectStatus = QtWidgets.QComboBox(self) self.selectStatus.addItems(['Disabled', 'Enabled']) self.selectStatus.currentIndexChanged.connect(self.changeStatus) self.setItemWidget(self.deviceStatus,1,self.selectStatus) self.modulationType = QtWidgets.QTreeWidgetItem(self.generalSettings) self.modulationType.setText(0, 'PWM modulation') self.modulationType.setIcon(0, GUIToolKit.getIconByName('gear')) self.selectModulation = QtWidgets.QComboBox(self) self.selectModulation.addItems(['Sine PWM', 'Space Vector PWM', 'Trapezoidal 120', 'Trapezoidal 150']) self.selectModulation.currentIndexChanged.connect(self.changeModType) self.setItemWidget(self.modulationType,1,self.selectModulation) self.modulationCenter = QtWidgets.QTreeWidgetItem(self.generalSettings) self.modulationCenter.setText(0, 'Modulation center') self.modulationCenter.setIcon(0, GUIToolKit.getIconByName('gear')) self.selectModCenter = QtWidgets.QComboBox(self) self.selectModCenter.addItems(['Disabled', 'Enabled']) self.selectModCenter.currentIndexChanged.connect(self.changeModCenter) self.setItemWidget(self.modulationCenter,1,self.selectModCenter) self.customComands = QtWidgets.QTreeWidgetItem(self.sFOCDevice) self.customComands.setText(0, 'Custom commands') self.customComands.setIcon(0, GUIToolKit.getIconByName('customcommands')) self.sFOCDevice.addChild(self.customComands) for customCommand in self.device.customCommands.customCommandsList: self.initCustomCommand(customCommand) self.installEventFilter(self) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.customCommandsMenu) self.header().resizeSection(0,230) self.setAlternatingRowColors(True) self.header().hide() self.expandItem(self.sFOCDevice) self.expandItem(self.motionControl) self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived) self.device.commProvider.stateMonitorReceived.connect(self.stateResponseReceived) self.itemChanged.connect(self.treeItemEdited) self.setEnabled(self.device.isConnected) def customCommandsMenu(self, position): indexes = self.selectedIndexes() if len(indexes) > 0: level = 0 index = indexes[0] while index.parent().isValid(): index = index.parent() level += 1 selectedItem = self.selectedItems()[0] menu = QtWidgets.QMenu() if selectedItem.text(0) == 'Custom commands': addComand = QtWidgets.QAction("Add command", self) addComand.triggered.connect(self.addCommandAction) menu.addAction(addComand) elif hasattr(selectedItem, 'isCustomCommand'): executeCommand = QtWidgets.QAction("Execute", self) executeCommand.triggered.connect(self.executeCustomCommandAction) menu.addAction(executeCommand) deleteCommand = QtWidgets.QAction("Remove", self) deleteCommand.triggered.connect(self.deleteCustomCommand) menu.addAction(deleteCommand) menu.exec_(self.viewport().mapToGlobal(position)) def addCommandAction(self): selectedItem = self.selectedItems()[0] self.addCustomCommand(selectedItem) def executeCustomCommandAction(self): selectedItem = self.selectedItems()[0] selectedCustomCommand = selectedItem.text(1) self.device.sendCommand(selectedCustomCommand) def deleteCustomCommand(self): root = self.invisibleRootItem() deletedIndex = self.customComands.indexOfChild(self.selectedItems()[0]) self.device.customCommands.customCommandsList.pop(deletedIndex) for item in self.selectedItems(): (item.parent() or root).removeChild(item) def eventFilter(self, obj, event): if event.type() == QtCore.QEvent.KeyPress: if event.key() == QtCore.Qt.Key_Return: selectedItem = self.selectedItems()[0] if selectedItem.text(0) == 'Custom commands': self.addCustomCommand(selectedItem) if event.key() == QtCore.Qt.Key_Space or event.key() == QtCore.Qt.Key_Right: selectedItem = self.selectedItems()[0] if selectedItem.parent().text(0) == 'Custom commands': self.executeCustomCommandAction() if event.key() == QtCore.Qt.Key_Delete or event.key() == QtCore.Qt.Key_Backspace: selectedItem = self.selectedItems()[0] if selectedItem.parent().text(0) == 'Custom commands': self.deleteCustomCommand() return super(DeviceTreeView, self).eventFilter(obj, event) def addCustomCommand(sefl,selectedItem): customCommand = QtWidgets.QTreeWidgetItem() customCommand.isCustomCommand = True customCommand.setText(0, 'Command') customCommand.setIcon(0, GUIToolKit.getIconByName('gear')) customCommand.setFlags( customCommand.flags() | QtCore.Qt.ItemIsEditable) selectedItem.addChild(customCommand) sefl.device.customCommands.customCommandsList.append(Command('Command','')) def initCustomCommand(sefl, command): customCommand = QtWidgets.QTreeWidgetItem() customCommand.isCustomCommand = True customCommand.setText(0, command.cmdName) customCommand.setText(1, command.cmd) customCommand.setIcon(0, GUIToolKit.getIconByName('gear')) customCommand.setFlags( customCommand.flags() | QtCore.Qt.ItemIsEditable) sefl.customComands.addChild(customCommand) def addPIDSubtree(self, parent, label): pidConfiguration = QtWidgets.QTreeWidgetItem() pidConfiguration.setText(0, label) pidConfiguration.setIcon(0, GUIToolKit.getIconByName('pidconfig')) parent.addChild(pidConfiguration) proportionalGain = QtWidgets.QTreeWidgetItem(pidConfiguration) proportionalGain.setText(0, 'Proportional gain') proportionalGain.setIcon(0, GUIToolKit.getIconByName('gear')) proportionalGain.setText(1, '') proportionalGain.setFlags( proportionalGain.flags() | QtCore.Qt.ItemIsEditable) integralGain = QtWidgets.QTreeWidgetItem(pidConfiguration) integralGain.setText(0, 'Integral gain') integralGain.setIcon(0, GUIToolKit.getIconByName('gear')) integralGain.setText(1, '') integralGain.setFlags( integralGain.flags() | QtCore.Qt.ItemIsEditable) derivativeGain = QtWidgets.QTreeWidgetItem(pidConfiguration) derivativeGain.setText(0, 'Derivative gain') derivativeGain.setIcon(0, GUIToolKit.getIconByName('gear')) derivativeGain.setText(1, '') derivativeGain.setFlags( derivativeGain.flags() | QtCore.Qt.ItemIsEditable) voltageRamp = QtWidgets.QTreeWidgetItem(pidConfiguration) voltageRamp.setText(0, 'Output Ramp') voltageRamp.setIcon(0, GUIToolKit.getIconByName('gear')) voltageRamp.setText(1, '') voltageRamp.setFlags( voltageRamp.flags() | QtCore.Qt.ItemIsEditable) limit = QtWidgets.QTreeWidgetItem(pidConfiguration) limit.setText(0, 'Output Limit') limit.setIcon(0, GUIToolKit.getIconByName('gear')) limit.setText(1, '') limit.setFlags( limit.flags() | QtCore.Qt.ItemIsEditable) lpfTf = QtWidgets.QTreeWidgetItem(pidConfiguration) lpfTf.setText(0, 'Low pass filter') lpfTf.setIcon(0, GUIToolKit.getIconByName('gear')) lpfTf.setText(1, '') lpfTf.setFlags( lpfTf.flags() | QtCore.Qt.ItemIsEditable) return pidConfiguration def treeItemEdited(self, item, column): if item.parent().text(0) == 'Custom commands': updatedIndex = self.customComands.indexOfChild(item) updatedValue = item.text(column) if column == 0: self.device.customCommands.customCommandsList[ updatedIndex].cmdName = updatedValue else: self.device.customCommands.customCommandsList[ updatedIndex].cmd = updatedValue else: self.sendCommand(item, column) def sendCommand(self, item, column): value = item.text(1) fieldName = item.text(0) pidLabel = item.parent().text(0) pid = {} lpf = {} if 'Velocity PID' in pidLabel: pid = self.device.PIDVelocity lpf = self.device.LPFVelocity elif 'Angle PID' in pidLabel: pid = self.device.PIDAngle lpf = self.device.LPFAngle elif 'Current q PID' in pidLabel: pid = self.device.PIDCurrentQ lpf = self.device.LPFCurrentQ elif 'Current d PID' in pidLabel: pid = self.device.PIDCurrentD lpf = self.device.LPFCurrentD if 'Proportional gain' in fieldName: self.device.sendProportionalGain(pid, value) elif 'Integral gain' in fieldName: self.device.sendIntegralGain(pid, value) elif 'Derivative gain' in fieldName: self.device.sendDerivativeGain(pid, value) elif 'Output Ramp' in fieldName: self.device.sendOutputRamp(pid, value) elif 'Low pass filter' in fieldName: self.device.sendLowPassFilter(lpf, value) elif 'Output Limit' in fieldName: self.device.sendOutputLimit(pid,value) elif 'Voltage limit' in fieldName: self.device.sendVoltageLimit(value) elif 'Velocity limit' in fieldName: self.device.sendVelocityLimit(value) elif 'Current limit' in fieldName: self.device.sendCurrentLimit(value) elif 'Phase Resistance' in fieldName: self.device.sendPhaseResistance(value) elif 'Zero Angle Offset' in fieldName: self.device.sendSensorZeroOffset(value) elif 'Electrical Zero Offset' in fieldName: self.device.sendSensorZeroElectrical(value) elif 'Motion Downsample' in fieldName: self.device.sendMotionDownsample(value) def refreshPIDSubtree(self, pidDisp, pidVal, lpfVal): pidDisp.child(0).setText(1,str(pidVal.P)) pidDisp.child(1).setText(1,str(pidVal.I)) pidDisp.child(2).setText(1,str(pidVal.D)) pidDisp.child(3).setText(1,str(pidVal.outputRamp)) pidDisp.child(4).setText(1,str(pidVal.outputLimit)) pidDisp.child(5).setText(1,str(lpfVal.Tf)) def commandResponseReceived(self, comandResponse): self.refreshDeviceTree() self.setTorqueMode(self.device.torqueType) self.setControlLopMode(self.device.controlType) self.setEnabledDisabled(self.device.deviceStatus) self.setModCenter(self.device.modulationCentered) self.setModType(self.device.modulationType) def stateResponseReceived(self, comandResponse): self.blockSignals(True) self.stateVel.setText(1,str(self.device.velocityNow)) self.stateAngle.setText(1,str(self.device.angleNow)) self.stateVd.setText(1,str(self.device.voltageDNow)) self.stateVq.setText(1,str(self.device.voltageQNow)) self.stateCq.setText(1,str(self.device.currentQNow)) self.stateCd.setText(1,str(self.device.currentDNow)) self.satateTarget.setText(1,str(self.device.targetNow)) self.update() self.blockSignals(False) def refreshDeviceTree(self): self.blockSignals(True) self.refreshPIDSubtree( self.PIDVelocityConfig, self.device.PIDVelocity, self.device.LPFVelocity) self.refreshPIDSubtree( self.PIDAngleConfig, self.device.PIDAngle, self.device.LPFAngle) self.refreshPIDSubtree( self.PIDCurrentQConfig, self.device.PIDCurrentQ, self.device.LPFCurrentQ) self.refreshPIDSubtree( self.PIDCurrentDConfig, self.device.PIDCurrentD, self.device.LPFCurrentD) self.voltageLimit.setText(1,str(self.device.voltageLimit)) self.velocityLimit.setText(1,str(self.device.velocityLimit)) self.currentLimit.setText(1,str(self.device.currentLimit)) self.sensorZeroOffset.setText(1,str(self.device.sensorZeroOffset)) self.sensorZeroElecOffset.setText(1,str(self.device.sensorElectricalZero)) self.phaseRes.setText(1,str(self.device.phaseResistance)) # self.deviceStatus.setText(1,str(self.device.deviceStatus)) self.motionDownsample.setText(1,str(self.device.motionDownsample)) # self.torque.setText(1,str(self.device.torqueType)) # self.controller.setText(1,str(self.device.controlType)) self.update() self.blockSignals(False) def setTorqueMode(self, value): self.blockSignals(True) if value == SimpleFOCDevice.VOLTAGE_TORQUE: self.selectorTorque.setCurrentIndex(0) elif value == SimpleFOCDevice.DC_CURRENT_TORQUE: self.selectorTorque.setCurrentIndex(1) elif value == SimpleFOCDevice.FOC_CURRENT_TORQUE: self.selectorTorque.setCurrentIndex(2) self.blockSignals(False) def changeTorque(self): index = self.selectorTorque.currentIndex() if index == 0: self.device.sendTorqueType(SimpleFOCDevice.VOLTAGE_TORQUE) elif index == 1: self.device.sendTorqueType(SimpleFOCDevice.DC_CURRENT_TORQUE) elif index == 2: self.device.sendTorqueType(SimpleFOCDevice.FOC_CURRENT_TORQUE) def setEnabledDisabled(self, value): self.blockSignals(True) if value == 0: self.selectStatus.setCurrentIndex(0) elif value == 1: self.selectStatus.setCurrentIndex(1) self.blockSignals(False) def changeStatus(self): index = self.selectStatus.currentIndex() if index == 0: self.device.sendDeviceStatus(0) elif index == 1: self.device.sendDeviceStatus(1) def setModCenter(self,value): self.blockSignals(True) self.selectModCenter.setCurrentIndex(int(value)) self.blockSignals(False) def changeModCenter(self): index = self.selectModCenter.currentIndex() if index == 0: self.device.sendModulationCentered(0) elif index == 1: self.device.sendModulationCentered(1) def setModType(self, value): self.blockSignals(True) if value == SimpleFOCDevice.SINE_PWM: self.selectModulation.setCurrentIndex(0) elif value == SimpleFOCDevice.SPACE_VECTOR_PWM: self.selectModulation.setCurrentIndex(1) elif value == SimpleFOCDevice.TRAPEZOIDAL_120: self.selectModulation.setCurrentIndex(2) elif value == SimpleFOCDevice.TRAPEZOIDAL_150: self.selectModulation.setCurrentIndex(3) self.blockSignals(False) def changeModType(self): index = self.selectModulation.currentIndex() if index == 0: self.device.sendModulationType(SimpleFOCDevice.SINE_PWM) elif index == 1: self.device.sendModulationType(SimpleFOCDevice.SPACE_VECTOR_PWM) elif index == 2: self.device.sendModulationType(SimpleFOCDevice.TRAPEZOIDAL_120) elif index == 3: self.device.sendModulationType(SimpleFOCDevice.TRAPEZOIDAL_150) def setControlLopMode(self, value): self.blockSignals(True) if value == SimpleFOCDevice.TORQUE_CONTROL: self.selectorControlLoop.setCurrentIndex(0) elif value == SimpleFOCDevice.VELOCITY_CONTROL: self.selectorControlLoop.setCurrentIndex(1) elif value == SimpleFOCDevice.ANGLE_CONTROL: self.selectorControlLoop.setCurrentIndex(2) elif value == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL: self.selectorControlLoop.setCurrentIndex(3) elif value == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL: self.selectorControlLoop.setCurrentIndex(4) self.blockSignals(False) def changeControlLoop(self): index = self.selectorControlLoop.currentIndex() if index == 0: self.device.sendControlType(SimpleFOCDevice.TORQUE_CONTROL) elif index == 1: self.device.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL) elif index == 2: self.device.sendControlType(SimpleFOCDevice.ANGLE_CONTROL) elif index == 3: self.device.sendControlType(SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL) elif index == 4: self.device.sendControlType(SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL) def connectionStateChanged(self, connectionFlag): if connectionFlag is True: self.setEnabled(True) else: self.setEnabled(False) ================================================ FILE: src/gui/configtool/devicesInspectorTree.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5.QtWidgets import (QVBoxLayout, QFrame) from src.gui.configtool.connectionControl import ConnectionControlGroupBox from src.gui.configtool.deviceJoggingControl import DeviceJoggingControl from src.gui.configtool.deviceTreeview import DeviceTreeView from src.gui.configtool.droDisplayWidget import DROGroupBox from src.gui.configtool.generalControls import GeneralControls from src.simpleFOCConnector import SimpleFOCDevice class DevicesInspectorTree(QFrame): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.droWidget = DROGroupBox(self) self.layout.addWidget(self.droWidget) self.generalControls = GeneralControls(self) self.layout.addWidget(self.generalControls) self.treeView = DeviceTreeView(self) self.layout.addWidget(self.treeView) self.joggingControl = DeviceJoggingControl(self) self.layout.addWidget(self.joggingControl) self.connectionControl = ConnectionControlGroupBox(self) self.layout.addWidget(self.connectionControl) self.setMaximumWidth(500) ================================================ FILE: src/gui/configtool/droDisplayWidget.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui,QtWidgets) from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class DROGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setTitle('Simple FOC Digital Read Out') self.setObjectName('digitalReadOut') self.droHorizontalLayout = QtWidgets.QHBoxLayout(self) self.droHorizontalLayout.setObjectName('droHorizontalLayout') self.signal0Label = QtWidgets.QLabel(self) self.signal0Label.setObjectName('angleLabel') self.signal0Label.setText('Angle') self.droHorizontalLayout.addWidget(self.signal0Label) self.signal0LCDNumber = QtWidgets.QLCDNumber(self) self.putStyleToLCDNumber(self.signal0LCDNumber) self.signal0LCDNumber.setObjectName('signal0LCDNumber') self.droHorizontalLayout.addWidget(self.signal0LCDNumber) self.signal1Label = QtWidgets.QLabel(self) self.signal1Label.setObjectName('velLabel') self.signal1Label.setText('Velocity') self.droHorizontalLayout.addWidget(self.signal1Label) self.signal1LCDNumber = QtWidgets.QLCDNumber(self) self.putStyleToLCDNumber(self.signal1LCDNumber) self.signal1LCDNumber.setObjectName('signal1LCDNumber') self.droHorizontalLayout.addWidget(self.signal1LCDNumber) self.signal2Label = QtWidgets.QLabel(self) self.signal2Label.setObjectName('torqueLabel') self.signal2Label.setText('Target') self.droHorizontalLayout.addWidget(self.signal2Label) self.signal2LCDNumber = QtWidgets.QLCDNumber(self) self.putStyleToLCDNumber(self.signal2LCDNumber) self.signal2LCDNumber.setObjectName('signal2LCDNumber') self.droHorizontalLayout.addWidget(self.signal2LCDNumber) self.signal3Label = QtWidgets.QLabel(self) self.signal3Label.setObjectName('targetLabel') self.signal3Label.setText('Target') self.droHorizontalLayout.addWidget(self.signal3Label) self.signal3LCDNumber = QtWidgets.QLCDNumber(self) self.putStyleToLCDNumber(self.signal3LCDNumber) self.signal3LCDNumber.setObjectName('signal3LCDNumber') self.droHorizontalLayout.addWidget(self.signal3LCDNumber) self.commandResponseReceived('init') self.initDiplay() self.disableUI() self.device.addConnectionStateListener(self) self.device.commProvider.stateMonitorReceived.connect(self.commandResponseReceived) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, isConnectedFlag): if isConnectedFlag is True: self.enabeUI() self.initDiplay() else: self.initDiplay() self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def initDiplay(self): self.signal0LCDNumber.display(0.0) self.signal1LCDNumber.display(0.0) self.signal2LCDNumber.display(0.0) self.signal3LCDNumber.display(0.0) def putStyleToLCDNumber(self, lcdNumber): lcdNumber.setStyleSheet('''QLCDNumber {background-color: white;}''') palette = self.setColor(lcdNumber.palette(), GUIToolKit.RED_COLOR) lcdNumber.setPalette(palette) def setColor(self, palette, colorTouple): R = colorTouple[0] G = colorTouple[1] B = colorTouple[2] # foreground color palette.setColor(palette.WindowText, QtGui.QColor(R, G, B)) # background color palette.setColor(palette.Background, QtGui.QColor(R, G, B)) # 'light' border palette.setColor(palette.Light, QtGui.QColor(R, G, B)) # 'dark' border palette.setColor(palette.Dark, QtGui.QColor(R, G, B)) return palette def commandResponseReceived(self, cmdRespose): if self.device.torqueType == SimpleFOCDevice.VOLTAGE_TORQUE: self.signal2Label.setText("Voltage") self.signal2LCDNumber.display(self.device.voltageQNow) else: # dc current or FOC current self.signal2Label.setText("Current") self.signal2LCDNumber.display(self.device.currentQNow) self.signal3LCDNumber.display(self.device.targetNow) self.signal1LCDNumber.display(self.device.velocityNow) self.signal0LCDNumber.display(self.device.angleNow) ================================================ FILE: src/gui/configtool/generalControls.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui, QtWidgets, QtCore) from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class GeneralControls(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) # self.setMaximumWidth(300) onlyFloat = QtGui.QRegExpValidator( QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+")) self.device = SimpleFOCDevice.getInstance() self.setTitle('General Controls') self.setObjectName('generalControls') self.gcGridLayout = QtWidgets.QGridLayout(self) self.gcGridLayout.setObjectName('gcGridLayout') self.enableDeviceButton = QtWidgets.QPushButton(self) self.enableDeviceButton.setObjectName('enButton') self.enableDeviceButton.setText('Enable Device') self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('greendot')) self.enableDeviceButton.clicked.connect(self.toggleEnable) self.gcGridLayout.addWidget(self.enableDeviceButton, 1, 0, 1, 1) self.sensorZeroButton = QtWidgets.QPushButton(self) self.sensorZeroButton.setObjectName('homeButton') self.sensorZeroButton.setText('Sensor Zero') self.sensorZeroButton.setIcon(GUIToolKit.getIconByName('home')) self.sensorZeroButton.clicked.connect(self.setSensorZero) self.gcGridLayout.addWidget(self.sensorZeroButton, 1, 1, 1, 1) self.setZeroTarget = QtWidgets.QPushButton(self) self.setZeroTarget.setObjectName('zeroButton') self.setZeroTarget.setText('Zero Target') self.setZeroTarget.setIcon(GUIToolKit.getIconByName('stop')) self.setZeroTarget.clicked.connect(self.setTargetZero) self.gcGridLayout.addWidget(self.setZeroTarget, 1, 2, 1, 1) self.reloadValues() self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def setSensorZero(self): val = self.device.angleNow + self.device.sensorZeroOffset self.device.sendSensorZeroOffset(val) def setTargetZero(self): self.device.sendTargetValue(0) def toggleEnable(self): val = int( not self.device.deviceStatus) self.device.sendDeviceStatus(val) def commandResponseReceived(self, comandResponse): self.reloadValues() def reloadValues(self): if self.device.deviceStatus: self.enableDeviceButton.setText('Disable Device') self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('reddot')) else: self.enableDeviceButton.setText('Enable Device') self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('greendot')) ================================================ FILE: src/gui/configtool/generalSettingsWidget.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui, QtWidgets, QtCore) from src.gui.sharedcomnponets.sharedcomponets import ConfigQLineEdit from src.simpleFOCConnector import SimpleFOCDevice class GeneralSettingsGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.setMaximumWidth(300) onlyFloat = QtGui.QRegExpValidator( QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+")) self.device = SimpleFOCDevice.getInstance() self.setTitle('General device settings') self.setObjectName('generalDeviceSettings') self.gcGridLayout = QtWidgets.QGridLayout(self) self.gcGridLayout.setObjectName('gcGridLayout') self.motionDownsample = QtWidgets.QLabel(self) self.motionDownsample.setObjectName('motionDownsample') self.motionDownsample.setText('Motion Downsample') self.gcGridLayout.addWidget(self.motionDownsample, 2, 0, 1, 1) self.motionDownsampleEdit = ConfigQLineEdit(self) self.motionDownsampleEdit.setObjectName('motionDownsampleEdit') self.motionDownsampleEdit.setValidator(onlyFloat) self.motionDownsampleEdit.setAlignment(QtCore.Qt.AlignCenter) self.motionDownsampleEdit.updateValue.connect(self.sendMotionDownsampleAction) self.gcGridLayout.addWidget(self.motionDownsampleEdit, 2, 1, 1, 1) self.curLimitLabel = QtWidgets.QLabel(self) self.curLimitLabel.setObjectName('curLimitLabel') self.curLimitLabel.setText('Current Limit') self.gcGridLayout.addWidget(self.curLimitLabel, 3, 0, 1, 1) self.velLimitlabel = QtWidgets.QLabel(self) self.velLimitlabel.setObjectName('velLimitlabel') self.velLimitlabel.setText('Velocity limit') self.gcGridLayout.addWidget(self.velLimitlabel, 4, 0, 1, 1) self.volLimitLabel = QtWidgets.QLabel(self) self.volLimitLabel.setObjectName('volLimitLabel') self.volLimitLabel.setText('Voltage limit') self.gcGridLayout.addWidget(self.volLimitLabel, 6, 0, 1, 1) self.clLineEdit = ConfigQLineEdit(self) self.clLineEdit.setObjectName('clLineEdit') self.clLineEdit.setValidator(onlyFloat) self.clLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.clLineEdit.updateValue.connect(self.sendCurrentLimitAction) self.gcGridLayout.addWidget(self.clLineEdit, 3, 1, 1, 1) self.vlLineEdit = ConfigQLineEdit(self) self.vlLineEdit.setObjectName('vlLineEdit') self.vlLineEdit.setValidator(onlyFloat) self.vlLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.vlLineEdit.updateValue.connect(self.sendVelLimitAction) self.gcGridLayout.addWidget(self.vlLineEdit, 4, 1, 1, 1) self.volLLineEdit = ConfigQLineEdit(self) self.volLLineEdit.setObjectName('volLLineEdit') self.volLLineEdit.setValidator(onlyFloat) self.volLLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.volLLineEdit.updateValue.connect(self.sendVoltageLimitAction) self.gcGridLayout.addWidget(self.volLLineEdit, 6, 1, 1, 1) self.reloadValues() self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def sendMotionDownsampleAction(self): value = self.motionDownsampleEdit.text() value = value.replace(',', '.') self.motionDownsampleEdit.setText(value) self.device.sendMotionDownsample(value) def sendCurrentLimitAction(self): value = self.clLineEdit.text() value = value.replace(',', '.') self.clLineEdit.setText(value) self.device.sendCurrentLimit(self.clLineEdit.text()) def sendVelLimitAction(self): value = self.vlLineEdit.text() value = value.replace(',', '.') self.vlLineEdit.setText(value) self.device.sendVelocityLimit(self.vlLineEdit.text()) def sendVoltageLimitAction(self): value = self.volLLineEdit.text() value = value.replace(',', '.') self.volLLineEdit.setText(value) self.device.sendVoltageLimit(self.volLLineEdit.text()) def commandResponseReceived(self, comandResponse): self.reloadValues() def reloadValues(self): self.motionDownsampleEdit.setText(str(self.device.motionDownsample)) self.clLineEdit.setText(str(self.device.currentLimit)) self.vlLineEdit.setText(str(self.device.velocityLimit)) self.volLLineEdit.setText(str(self.device.voltageLimit)) ================================================ FILE: src/gui/configtool/generatedCodeDisplay.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from PyQt5.QtCore import QRegExp from PyQt5.QtCore import Qt from PyQt5.QtGui import (QSyntaxHighlighter,QTextCharFormat,QBrush,QColor) from PyQt5.QtWidgets import (QVBoxLayout) from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget, GUIToolKit) from src.simpleFOCConnector import SimpleFOCDevice class GenerateCodeDialog(QtWidgets.QDialog): def __init__(self, parent=None): super().__init__(parent=parent) self.setWindowTitle("Generate Code") self.setWindowIcon(GUIToolKit.getIconByName('gen')) self.checkBoxMotionControl = QtWidgets.QCheckBox(self) self.checkBoxMotionControl.setObjectName('motion') self.checkBoxMotionControl.setText("Torque/Motion control") self.checkBoxMotionControl.setIcon(GUIToolKit.getIconByName('motor')) self.checkBoxMotionControl.setChecked(True) self.checkBoxPidVel = QtWidgets.QCheckBox(self) self.checkBoxPidVel.setObjectName('pidVel') self.checkBoxPidVel.setText("PID velocity") self.checkBoxPidVel.setIcon(GUIToolKit.getIconByName('pidconfig')) self.checkBoxPidVel.setChecked(True) self.checkBoxPidAngle = QtWidgets.QCheckBox(self) self.checkBoxPidAngle.setObjectName('pidAngle') self.checkBoxPidAngle.setText("PID angle") self.checkBoxPidAngle.setIcon(GUIToolKit.getIconByName('pidconfig')) self.checkBoxPidAngle.setChecked(True) self.checkBoxPidCq = QtWidgets.QCheckBox(self) self.checkBoxPidCq.setObjectName('pidCq') self.checkBoxPidCq.setText("PID current q") self.checkBoxPidCq.setIcon(GUIToolKit.getIconByName('pidconfig')) self.checkBoxPidCq.setChecked(True) self.checkBoxPidCd = QtWidgets.QCheckBox(self) self.checkBoxPidCd.setObjectName('pidCq') self.checkBoxPidCd.setText("PID current d") self.checkBoxPidCd.setIcon(GUIToolKit.getIconByName('pidconfig')) self.checkBoxPidCd.setChecked(True) self.checkBoxLimits = QtWidgets.QCheckBox(self) self.checkBoxLimits.setObjectName('limits') self.checkBoxLimits.setText("Limits") self.checkBoxLimits.setIcon(GUIToolKit.getIconByName('statistics')) self.checkBoxLimits.setChecked(True) self.checkBoxPhaseRes = QtWidgets.QCheckBox(self) self.checkBoxPhaseRes.setObjectName('phaseRes') self.checkBoxPhaseRes.setText("Phase Resistance") self.checkBoxPhaseRes.setIcon(GUIToolKit.getIconByName('res')) self.checkBoxPhaseRes.setChecked(True) self.checkBoxModulation = QtWidgets.QCheckBox(self) self.checkBoxModulation.setObjectName('modulation') self.checkBoxModulation.setText("PWM Modulation") self.checkBoxModulation.setIcon(GUIToolKit.getIconByName('gear')) self.checkBoxModulation.setChecked(True) self.sensorOffset = QtWidgets.QCheckBox(self) self.sensorOffset.setObjectName('sensorOffset') self.sensorOffset.setText("Sensor Offset") self.sensorOffset.setIcon(GUIToolKit.getIconByName('gear')) self.sensorOffset.setChecked(True) self.sensorElOffset = QtWidgets.QCheckBox(self) self.sensorElOffset.setObjectName('sensorOffset') self.sensorElOffset.setToolTip('Be careful!
Only for absolute sensors') self.sensorElOffset.setText("Sensor Electrical Offset") self.sensorElOffset.setIcon(GUIToolKit.getIconByName('gear')) self.sensorElOffset.setChecked(False) QBtn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel self.buttonBox = QtWidgets.QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) text = "

Arduino Code Generation

" text += "Arduino code generation for the motor parameters currently used in the SimpleFOCStudio
" text += "Once you are happy with the performance of your system you can generate the arduino code of the parameters you have tuned.
" text += "The generated code you can just copy/paste in your setup() function, just before calling the motor.init()
" text += "

Choose the parameter sets to be generated:

" self.layout = QtWidgets.QVBoxLayout() message1 = QtWidgets.QLabel(text) self.layout.addWidget(message1) self.layout.addWidget(self.checkBoxMotionControl) self.layout.addWidget(self.checkBoxPidVel) self.layout.addWidget(self.checkBoxPidAngle) self.layout.addWidget(self.checkBoxPidCq) self.layout.addWidget(self.checkBoxPidCd) self.layout.addWidget(self.checkBoxLimits) self.layout.addWidget(self.checkBoxPhaseRes) self.layout.addWidget(self.checkBoxModulation) self.layout.addWidget(self.sensorOffset) self.layout.addWidget(self.sensorElOffset) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) class GeneratedCodeDisplay(WorkAreaTabWidget): def __init__(self, parent=None ): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() dlg = GenerateCodeDialog() # If you pass self, the dialog will be centered over the main window as before. if dlg.exec_(): toGenerate=[ dlg.checkBoxMotionControl.isChecked(), dlg.checkBoxPidVel.isChecked(), dlg.checkBoxPidAngle.isChecked(), dlg.checkBoxPidCq.isChecked(), dlg.checkBoxPidCd.isChecked(), dlg.checkBoxLimits.isChecked(), dlg.sensorOffset.isChecked(), dlg.sensorElOffset.isChecked(), dlg.checkBoxPhaseRes.isChecked(), dlg.checkBoxModulation.isChecked(), ] code = self.device.toArduinoCode(toGenerate) self.layout = QVBoxLayout(self) text = "

Generated Arduino Code

" text += "This generated code you can just copy/paste into your setup() function, it is important that you place it before calling the motor.init()
" message1 = QtWidgets.QLabel(text) self.layout.addWidget(message1) self.codeDisplayBefore = QtWidgets.QLabel(self) self.layout.addWidget(self.codeDisplayBefore) code0 = '#include <SimpleFOC.h>' code0 +="
...
" code0 += 'void setup() {
' code0 += " ...." self.codeDisplayBefore.setText(code0) self.codeDisplayBefore.setTextFormat(Qt.TextFormat.RichText) self.codeDisplay = QtWidgets.QTextEdit(self) self.codeDisplay.setObjectName('codeDisplay') self.codeDisplay.setText(code) highlighter = MyHighlighter( self.codeDisplay, "Classic" ) self.layout.addWidget(self.codeDisplay) self.codeDisplayAfter = QtWidgets.QLabel(self) self.layout.addWidget(self.codeDisplayAfter) code1 = '// initialize motor
' code1 += 'motor.init();
' code1 += '// align sensor and start FOC
' code1 += 'motor.initFOC();
...
}' code1 += '
void loop() {' code1 += '
....
' code1 += 'motor.move();
' code1 += 'motor.loopFOC();
' code1 += '....
}' # MyHighlighter( self.codeDisplayAfter, "Classic" ) self.codeDisplayAfter.setText(code1) self.codeDisplayAfter.setTextFormat(Qt.TextFormat.RichText) self.setLayout(self.layout) def getTabIcon(self): return GUIToolKit.getIconByName('gen') def getTabName(self): return 'Generated Code' class MyHighlighter( QSyntaxHighlighter ): def __init__( self, parent, theme ): QSyntaxHighlighter.__init__( self, parent ) self.parent = parent keyword = QTextCharFormat() comment = QTextCharFormat() self.highlightingRules = [] # keyword brush = QBrush(QColor(211,84,0), Qt.SolidPattern ) keyword.setForeground( brush ) keywords = list( [ "motor",'FOCModulationType','MotionControlType','TorqueControlType'] ) for word in keywords: pattern = QRegExp("\\b" + word + "\\b") rule = HighlightingRule( pattern, keyword ) self.highlightingRules.append( rule ) # comment brush = QBrush( Qt.gray, Qt.SolidPattern ) pattern = QRegExp( "\/\/.*[^\n]" ) comment.setForeground( brush ) comment.setFontItalic( True ) rule = HighlightingRule( pattern, comment ) self.highlightingRules.append( rule ) def highlightBlock( self, text ): for rule in self.highlightingRules: expression = QRegExp( rule.pattern ) index = expression.indexIn( text ) while index >= 0: length = expression.matchedLength() self.setFormat( index, length, rule.format ) index = expression.indexIn( text, index + length ) self.setCurrentBlockState( 0 ) class HighlightingRule(): def __init__( self, pattern, format ): self.pattern = pattern self.format = format ================================================ FILE: src/gui/configtool/graphicWidget.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import logging import numpy as np import pyqtgraph as pg from PyQt5 import QtWidgets from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class SimpleFOCGraphicWidget(QtWidgets.QGroupBox): disconnectedState = 0 initialConnectedState = 1 connectedPausedState = 2 connectedPlottingStartedState = 3 signals = ['Target', 'Vq','Vd','Cq','Cd','Vel','Angle'] signal_tooltip = ['Target', 'Voltage Q [Volts]','Voltage D [Volts]','Current Q [miliAmps]','Current D [miliAmps]','Velocity [rad/sec]','Angle [rad]'] signalColors = [GUIToolKit.RED_COLOR, GUIToolKit.BLUE_COLOR, GUIToolKit.PURPLE_COLOR,GUIToolKit.YELLOW_COLOR, GUIToolKit.MAROON_COLOR, GUIToolKit.ORANGE_COLOR, GUIToolKit.GREEN_COLOR] signalIcons = ['reddot', 'bluedot','purpledot', 'yellowdot', 'maroondot', 'orangedot', 'greendot'] def __init__(self, parent=None): super().__init__(parent) self.setObjectName('plotWidget') self.setTitle('Real time motor variables: ') self.horizontalLayout = QtWidgets.QVBoxLayout(self) self.device = SimpleFOCDevice.getInstance() self.numberOfSamples = 300 pg.setConfigOptions(antialias=True) self.plotWidget = pg.PlotWidget() self.plotWidget.showGrid(x=True, y=True, alpha=0.5) self.plotWidget.addLegend() # self.legend = pg.LegendItem() # self.legend.setParentItem(self.plotWidget) self.timeArray = np.arange(-self.numberOfSamples, 0, 1) self.controlPlotWidget = ControlPlotPanel(controllerPlotWidget=self) self.signalDataArrays = [] self.signalPlots = [] self.signalPlotFlags = [] for (sig, sigColor, checkBox, tooltip) in zip(self.signals, self.signalColors,self.controlPlotWidget.signalCheckBox, self.signal_tooltip): # define signal plot data array self.signalDataArrays.append(np.zeros(self.numberOfSamples)) # configure signal plot parameters signalPen = pg.mkPen(color=sigColor, width=1.5) self.signalPlots.append(pg.PlotDataItem(self.timeArray, self.signalDataArrays[-1], pen=signalPen, name=tooltip)) self.plotWidget.addItem(self.signalPlots[-1]) # is plotted flag self.signalPlotFlags.append(True) # add callback checkBox.stateChanged.connect(self.signalPlotFlagUpdate) self.horizontalLayout.addWidget(self.plotWidget) self.horizontalLayout.addWidget(self.controlPlotWidget) self.device.commProvider.monitoringDataReceived.connect( self.upDateGraphic) self.currentStatus = self.disconnectedState self.controlPlotWidget.pauseContinueButton.setDisabled(True) self.device.addConnectionStateListener(self) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.currentStatus = self.initialConnectedState self.enabeUI() else: self.controlPlotWidget.startStoPlotAction() self.controlPlotWidget.stopAndResetPlot() self.currentStatus = self.disconnectedState self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def signalPlotFlagUpdate(self): self.controlPlotWidget.updateMonitorVariables() for i, (checkBox, plotFlag) in enumerate(zip(self.controlPlotWidget.signalCheckBox, self.signalPlotFlags)): if checkBox.isChecked() and (not plotFlag): self.signalPlotFlags[i] = True self.plotWidget.addItem( self.signalPlots[i] ) elif (not checkBox.isChecked()) and plotFlag: self.signalPlotFlags[i] = False self.plotWidget.removeItem( self.signalPlots[i] ) def connectioStatusUpdate(self, connectedFlag): if connectedFlag: self.currentStatus = self.initialConnectedState else: self.currentStatus = self.disconnectedState def upDateGraphic(self, signalList): if self.currentStatus is self.connectedPlottingStartedState or \ self.currentStatus is self.connectedPausedState: signals = np.array(signalList, dtype=float) signalIndex = 0 enabled = np.where(np.array(self.signalPlotFlags) == True)[0] if(len(enabled) != len(signals)): logging.warning('Arrived corrupted data') return else: for i, ind in enumerate(enabled): self.signalDataArrays[ind] = np.roll(self.signalDataArrays[ind], -1) self.signalDataArrays[ind][-1] = signals[i] if self.currentStatus is self.connectedPlottingStartedState: self.updatePlot() def computeStatic(self, array): mean = np.mean(array) std = np.std(array) max = np.max(array) min = np.min(array) meadian = np.median(array) def updatePlot(self): for i, plotFlag in enumerate(self.signalPlotFlags): if plotFlag: self.signalPlots[i].setData(self.timeArray, self.signalDataArrays[i]) self.signalPlots[i].updateItems() self.signalPlots[i].sigPlotChanged.emit(self.signalPlots[i]) class ControlPlotPanel(QtWidgets.QWidget): def __init__(self, parent=None, controllerPlotWidget=None): '''Constructor for ToolsWidget''' super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.controlledPlot = controllerPlotWidget self.verticalLayout = QtWidgets.QVBoxLayout(self) self.setLayout(self.verticalLayout) self.horizontalLayout1 = QtWidgets.QHBoxLayout() self.horizontalLayout1.setObjectName('horizontalLayout') self.startStopButton = QtWidgets.QPushButton(self) self.startStopButton.setText('Start') self.startStopButton.setObjectName('Start') self.startStopButton.clicked.connect(self.startStoPlotAction) self.startStopButton.setIcon(GUIToolKit.getIconByName('start')) self.horizontalLayout1.addWidget(self.startStopButton) self.pauseContinueButton = QtWidgets.QPushButton(self) self.pauseContinueButton.setObjectName('pauseButton') self.pauseContinueButton.setText('Pause') self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause')) self.pauseContinueButton.clicked.connect(self.pauseContinuePlotAction) self.horizontalLayout1.addWidget(self.pauseContinueButton) self.zoomAllButton = QtWidgets.QPushButton(self) self.zoomAllButton.setObjectName('zoomAllButton') self.zoomAllButton.setText('View all') self.zoomAllButton.setIcon(GUIToolKit.getIconByName('zoomall')) self.zoomAllButton.clicked.connect(self.zoomAllPlot) self.horizontalLayout1.addWidget(self.zoomAllButton) self.signalCheckBox = [] for i in range(len(self.controlledPlot.signals)): checkBox = QtWidgets.QCheckBox(self) checkBox.setObjectName('signalCheckBox'+str(i)) checkBox.setToolTip(self.controlledPlot.signal_tooltip[i]) checkBox.setText(self.controlledPlot.signals[i]) checkBox.setIcon(GUIToolKit.getIconByName(self.controlledPlot.signalIcons[i])) checkBox.setChecked(True) self.signalCheckBox.append(checkBox) self.horizontalLayout1.addWidget(checkBox) spacerItem = QtWidgets.QSpacerItem(100, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) self.horizontalLayout1.addItem(spacerItem) self.horizontalLayout1.addItem(spacerItem) self.downsampleLabel = QtWidgets.QLabel(self) self.downsampleLabel.setText('Downsample') self.downampleValue = QtWidgets.QLineEdit(self.downsampleLabel) self.downampleValue.setText("100") self.downampleValue.editingFinished.connect(self.changeDownsampling) self.horizontalLayout1.addWidget(self.downsampleLabel) self.horizontalLayout1.addWidget(self.downampleValue) self.verticalLayout.addLayout(self.horizontalLayout1) def startStoPlotAction(self): if self.controlledPlot.currentStatus is self.controlledPlot.initialConnectedState: # Start pressed self.startStopButton.setText('Stop') self.startStopButton.setIcon(GUIToolKit.getIconByName('stop')) self.controlledPlot.currentStatus = \ self.controlledPlot.connectedPlottingStartedState self.pauseContinueButton.setEnabled(True) self.device.sendMonitorDownsample(int(self.downampleValue.text())) self.updateMonitorVariables() else: # Stop pressed self.startStopButton.setText('Start') self.startStopButton.setIcon(GUIToolKit.getIconByName('start')) self.pauseContinueButton.setText('Pause') self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause')) self.pauseContinueButton.setEnabled(False) self.stopAndResetPlot() self.device.sendMonitorDownsample(0) self.device.sendMonitorClearVariables() def pauseContinuePlotAction(self): if self.controlledPlot.currentStatus is self.controlledPlot.connectedPausedState: # Continue pressed self.pauseContinueButton.setText('Pause') self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause')) self.controlledPlot.currentStatus = self.controlledPlot.connectedPlottingStartedState else: # Pause pressed self.pauseContinueButton.setText('Continue') self.pauseContinueButton.setIcon( GUIToolKit.getIconByName('continue')) self.controlledPlot.currentStatus = self.controlledPlot.connectedPausedState def stopAndResetPlot(self): self.controlledPlot.currentStatus = self.controlledPlot.initialConnectedState for dataArray in self.controlledPlot.signalDataArrays: dataArray = np.zeros(self.controlledPlot.numberOfSamples) def zoomAllPlot(self): self.controlledPlot.plotWidget.enableAutoRange() def changeDownsampling(self): if self.controlledPlot.currentStatus == self.controlledPlot.connectedPlottingStartedState: self.device.sendMonitorDownsample(int(self.downampleValue.text())) def updateMonitorVariables(self): self.device.sendMonitorVariables([self.signalCheckBox[0].isChecked(), self.signalCheckBox[1].isChecked(), self.signalCheckBox[2].isChecked(), self.signalCheckBox[3].isChecked(), self.signalCheckBox[4].isChecked(), self.signalCheckBox[5].isChecked(), self.signalCheckBox[6].isChecked()]) ================================================ FILE: src/gui/configtool/pidConfiguration.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui, QtWidgets, QtCore) from src.gui.sharedcomnponets.sharedcomponets import ConfigQLineEdit from src.simpleFOCConnector import SimpleFOCDevice class PidGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.setMaximumWidth(300) onlyFloatInputValidator = QtGui.QRegExpValidator( QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+")) self.device = SimpleFOCDevice.getInstance() self.activePID = self.device.PIDVelocity self.activeLPF = self.device.LPFVelocity self.setObjectName('pidConfigurator') self.setTitle('PID Controller configuration') self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.setObjectName('gridLayout') self.sPidLabel = QtWidgets.QLabel(self) self.sPidLabel.setObjectName('pgLabel') self.sPidLabel.setText('Select PID') self.gridLayout.addWidget(self.sPidLabel, 0, 1, 1, 1) self.selectorPIDF = QtWidgets.QComboBox(self) self.selectorPIDF.setObjectName('selectPIDF') self.selectorPIDF.addItems(['Velocity', 'Angle', 'Current Q', 'Current D']) self.selectorPIDF.currentIndexChanged.connect(self.changePIDF) self.gridLayout.addWidget(self.selectorPIDF, 0, 2, 1, 1) self.pgLabel = QtWidgets.QLabel(self) self.pgLabel.setObjectName('pgLabel') self.pgLabel.setText('Proportional gain') self.gridLayout.addWidget(self.pgLabel, 1, 1, 1, 1) self.iglabel = QtWidgets.QLabel(self) self.iglabel.setObjectName('iglabel') self.iglabel.setText('Integral gain') self.gridLayout.addWidget(self.iglabel, 2, 1, 1, 1) self.dgLabel = QtWidgets.QLabel(self) self.dgLabel.setObjectName('dgLabel') self.dgLabel.setText('Derivative gain') self.gridLayout.addWidget(self.dgLabel, 3, 1, 1, 1) self.vrLabel = QtWidgets.QLabel(self) self.vrLabel.setObjectName('vrLabel') self.vrLabel.setText('Output ramp') self.gridLayout.addWidget(self.vrLabel, 4, 1, 1, 1) self.vrLabel = QtWidgets.QLabel(self) self.vrLabel.setObjectName('lpfLabel') self.vrLabel.setText('Low pass filter') self.gridLayout.addWidget(self.vrLabel, 5, 1, 1, 1) self.pgLineEdit = ConfigQLineEdit(self) self.pgLineEdit.setObjectName('pgLineEdit') self.pgLineEdit.setValidator(onlyFloatInputValidator) self.pgLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.pgLineEdit.editingFinished.connect(self.sendProportionalGainAction) self.gridLayout.addWidget(self.pgLineEdit, 1, 2, 1, 1) self.igLineEdit = ConfigQLineEdit(self) self.igLineEdit.setObjectName('igLineEdit') self.igLineEdit.setValidator(onlyFloatInputValidator) self.igLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.igLineEdit.editingFinished.connect(self.sendIntegralGainAction) self.gridLayout.addWidget(self.igLineEdit, 2, 2, 1, 1) self.dgLineEdit = ConfigQLineEdit(self) self.dgLineEdit.setObjectName('dgLineEdit') self.dgLineEdit.setValidator(onlyFloatInputValidator) self.dgLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.dgLineEdit.editingFinished.connect(self.sendDerivativeGainAction) self.gridLayout.addWidget(self.dgLineEdit, 3, 2, 1, 1) self.vrLineEdit = ConfigQLineEdit(self) self.vrLineEdit.setObjectName('vrLineEdit') self.vrLineEdit.setValidator(onlyFloatInputValidator) self.vrLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.vrLineEdit.editingFinished.connect(self.sendRampAction) self.gridLayout.addWidget(self.vrLineEdit, 4, 2, 1, 1) self.lpfLineEdit = ConfigQLineEdit(self) self.lpfLineEdit.setObjectName('lpfLineEdit') self.lpfLineEdit.setValidator(onlyFloatInputValidator) self.lpfLineEdit.setAlignment(QtCore.Qt.AlignCenter) self.lpfLineEdit.editingFinished.connect(self.sendLPFAction) self.gridLayout.addWidget(self.lpfLineEdit, 5, 2, 1, 1) self.reloadPIDValues() # self.setDerivativeGainAction(self.device.derivativeGainPID) # self.setProportionalGainAction(self.device.proportionalGainPID) # self.setIntegralGainAction(self.device.integralGainPID) # self.setVoltageRampAction(self.device.voltageRampPID) self.connectionStateChanged(self.device.isConnected) self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def sendDerivativeGainAction(self): value = self.dgLineEdit.text() value = value.replace(',', '.') self.dgLineEdit.setText(value) self.device.sendDerivativeGain(self.activePID, value) def sendProportionalGainAction(self): value = self.pgLineEdit.text() value = value.replace(',', '.') self.pgLineEdit.setText(value) self.device.sendProportionalGain(self.activePID, self.pgLineEdit.text()) def sendIntegralGainAction(self): value = self.igLineEdit.text() value = value.replace(',', '.') self.igLineEdit.setText(value) self.device.sendIntegralGain(self.activePID, self.igLineEdit.text()) def sendRampAction(self): value = self.vrLineEdit.text() value = value.replace(',', '.') self.vrLineEdit.setText(value) self.device.sendOutputRamp(self.activePID, self.vrLineEdit.text()) def sendLPFAction(self): value = self.lpfLineEdit.text() value = value.replace(',', '.') self.lpfLineEdit.setText(value) self.device.sendLowPassFilter(self.activeLPF, self.lpfLineEdit.text()) def reloadPIDValues(self): self.pgLineEdit.setText(str(self.activePID.P)) self.dgLineEdit.setText(str(self.activePID.D)) self.igLineEdit.setText(str(self.activePID.I)) self.vrLineEdit.setText(str(self.activePID.outputRamp)) self.lpfLineEdit.setText(str(self.activeLPF.Tf)) def changePIDF(self): index = self.selectorPIDF.currentIndex() if index == 0: self.activePID = self.device.PIDVelocity self.activeLPF = self.device.LPFVelocity elif index == 1: self.activePID = self.device.PIDAngle self.activeLPF = self.device.LPFAngle elif index == 2: self.activePID = self.device.PIDCurrentQ self.activeLPF = self.device.LPFCurrentQ elif index == 3: self.activePID = self.device.PIDCurrentD self.activeLPF = self.device.LPFCurrentD self.device.pullPIDConf(self.activePID, self.activeLPF) self.reloadPIDValues() def commandResponseReceived(self, commandDataReceived): self.pgLineEdit.setText(str(self.activePID.P)) self.dgLineEdit.setText(str(self.activePID.D)) self.igLineEdit.setText(str(self.activePID.I)) self.vrLineEdit.setText(str(self.activePID.outputRamp)) self.lpfLineEdit.setText(str(self.activeLPF.Tf)) ================================================ FILE: src/gui/configtool/torqueConfig.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.simpleFOCConnector import SimpleFOCDevice class TorqueGroupBox(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('torqueMode') self.setTitle('Torque Mode') self.torqueTypeHorizontalLayout = QtWidgets.QHBoxLayout(self) self.torqueTypeHorizontalLayout.setObjectName('torqueHorizontalLayout') self.selectorTorque = QtWidgets.QComboBox(self) self.selectorTorque.setObjectName('selectorControlLoop') self.selectorTorque.addItems(['Voltage', 'DC Current', 'FOC Current']) self.selectorTorque.currentIndexChanged.connect(self.changeTorque) self.torqueTypeHorizontalLayout.addWidget(self.selectorTorque) self.setTorqueMode(self.device.torqueType) self.disableUI() self.device.addConnectionStateListener(self) self.device.commProvider.commandDataReceived.connect( self.commandResponseReceived) self.connectionStateChanged(self.device.isConnected) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() else: self.disableUI() def enabeUI(self): self.setEnabled(True) def disableUI(self): self.setEnabled(False) def setTorqueMode(self, value): if value == SimpleFOCDevice.VOLTAGE_TORQUE: self.selectorTorque.setCurrentIndex(0) elif value == SimpleFOCDevice.DC_CURRENT_TORQUE: self.selectorTorque.setCurrentIndex(1) elif value == SimpleFOCDevice.FOC_CURRENT_TORQUE: self.selectorTorque.setCurrentIndex(2) def changeTorque(self): index = self.selectorTorque.currentIndex() if index == 0: self.device.sendTorqueType(SimpleFOCDevice.VOLTAGE_TORQUE) elif index == 1: self.device.sendTorqueType(SimpleFOCDevice.DC_CURRENT_TORQUE) elif index == 2: self.device.sendTorqueType(SimpleFOCDevice.FOC_CURRENT_TORQUE) def commandResponseReceived(self, cmdRespose): self.setTorqueMode(self.device.torqueType) ================================================ FILE: src/gui/configtool/treeViewConfigTool.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QVBoxLayout, QSplitter) from src.gui.configtool.deviceInteractionFrame import DeviceInteractionFrame from src.gui.configtool.devicesInspectorTree import DevicesInspectorTree from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget, GUIToolKit) from src.simpleFOCConnector import SimpleFOCDevice class TreeViewConfigTool(WorkAreaTabWidget): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.layout = QVBoxLayout(self) self.treeViewWidget = DevicesInspectorTree(self) self.leftWidget = DeviceInteractionFrame(self) self.verticalSplitter = QSplitter(Qt.Horizontal) self.verticalSplitter.addWidget(self.treeViewWidget) self.verticalSplitter.addWidget(self.leftWidget) self.layout.addWidget(self.verticalSplitter) self.setLayout(self.layout) def getTabIcon(self): return GUIToolKit.getIconByName('motor') def getTabName(self): return self.device.connectionID ================================================ FILE: src/gui/mainWindow.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtCore, QtWidgets) from src.gui.toolbar import SimpleFOCConfigToolBar from src.gui.workAreaTabbedWidget import WorkAreaTabbedWidget class UserInteractionMainWindow(object): def setupUi(self, main_window): main_window.setObjectName('MainWindow') main_window.resize(1300, 900) main_window.setWindowTitle('SimpleFOC Configuration Tool ') self.centralwidget = QtWidgets.QWidget(main_window) self.centralwidget.setObjectName('centralwidget') # Add layout de to the main window self.horizontalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName('verticalLayout') # Add tabebd tools widget to the main window self.tabbedToolsWidget = WorkAreaTabbedWidget(self.centralwidget) self.horizontalLayout.addWidget(self.tabbedToolsWidget) # Add toolbar to the main window self.toolBar = SimpleFOCConfigToolBar(main_window,self.tabbedToolsWidget, main_window) main_window.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) # Add status bar to the main window self.statusbar = QtWidgets.QStatusBar(main_window) self.statusbar.setObjectName('statusbar') main_window.setStatusBar(self.statusbar) # Add central Widget to the main window main_window.setCentralWidget(self.centralwidget) ================================================ FILE: src/gui/sharedcomnponets/commandLineInterface.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import (QtGui, QtWidgets) from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit from src.simpleFOCConnector import SimpleFOCDevice class CommandLineWidget(QtWidgets.QGroupBox): def __init__(self, parent=None): super().__init__(parent) self.device = SimpleFOCDevice.getInstance() self.setObjectName('groupBox') self.setTitle('Command Line interface') self.cmlVerticalLayout = QtWidgets.QVBoxLayout(self) self.cmlVerticalLayout.setObjectName('cmlVerticalLayout') self.commandLineDisplay = QtWidgets.QTextEdit(self) self.commandLineDisplay.setObjectName('commandLineDisplay') self.cmlVerticalLayout.addWidget(self.commandLineDisplay) self.commandLineDisplay.setReadOnly(True) self.commandLineDisplay.setTextColor(QtGui.QColor(68, 117, 68, 255)) self.cmlWidget = QtWidgets.QWidget(self) self.cmlWidget.setObjectName('cmlWidget') self.cmlHorizontalLayout = QtWidgets.QHBoxLayout(self.cmlWidget) self.cmlHorizontalLayout.setObjectName('cmlHorizontalLayout') self.commandLineEdit = QtWidgets.QLineEdit(self.cmlWidget) self.commandLineEdit.setObjectName('commandLineEdit') self.cmlHorizontalLayout.addWidget(self.commandLineEdit) self.commandLineEdit.returnPressed.connect(self.sendAction) self.sendButton = QtWidgets.QPushButton(self.cmlWidget) self.sendButton.setObjectName('sendButton') self.sendButton.setText('Send') self.sendButton.setIcon(GUIToolKit.getIconByName('send')) self.sendButton.clicked.connect(self.sendAction) self.cmlHorizontalLayout.addWidget(self.sendButton) self.clearButton = QtWidgets.QPushButton(self.cmlWidget) self.clearButton.setObjectName('clearButton') self.cmlHorizontalLayout.addWidget(self.clearButton) self.clearButton.setIcon(GUIToolKit.getIconByName('delete')) self.clearButton.clicked.connect(self.clearAction) self.clearButton.setText('Clear') self.listDevices = QtWidgets.QPushButton(self.cmlWidget) self.listDevices.setObjectName('listDevices') self.cmlHorizontalLayout.addWidget(self.listDevices) self.listDevices.setIcon(GUIToolKit.getIconByName('list')) self.listDevices.clicked.connect(self.sendListDevices) self.listDevices.setText('List Devices') self.cmlVerticalLayout.addWidget(self.cmlWidget) self.device.addConnectionStateListener(self) self.setEnabled(False) def connectionStateChanged(self, deviceConnected): if deviceConnected is True: self.enabeUI() self.publishCommandResponseData('Connected ...') else: self.disableUI() self.publishCommandResponseData('Disconnected ...') def enabeUI(self): self.commandLineDisplay.setEnabled(True) self.cmlWidget.setEnabled(True) self.commandLineEdit.setEnabled(True) self.sendButton.setEnabled(True) self.setEnabled(True) def disableUI(self): # self.commandLineDisplay.setEnabled(False) self.commandLineEdit.setEnabled(False) self.sendButton.setEnabled(False) def publishCommandResponseData(self, data): self.commandLineDisplay.append(data) self.commandLineDisplay.moveCursor(QtGui.QTextCursor.End) def clearAction(self): self.commandLineDisplay.setPlainText('') def sendAction(self): self.device.sendCommand(self.commandLineEdit.text()) self.commandLineEdit.setText('') def sendListDevices(self): self.device.sendCommand('?') ================================================ FILE: src/gui/sharedcomnponets/sharedcomponets.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from PyQt5 import (QtGui, QtWidgets,QtCore) from serial.tools import list_ports class GUIToolKit(object): ''' This class is used to provide icons for the rest of the application hiding the location of the resources ''' RED_COLOR = (255, 92, 92) GREEN_COLOR = (57, 217, 138) BLUE_COLOR = (91, 141, 236) ORANGE_COLOR = (253, 172, 66) YELLOW_COLOR = (255,255,51) PURPLE_COLOR = (75,0,130) MAROON_COLOR = (222,184,135) @staticmethod def getIconByName(icoName): file_index = { 'add': 'add.png', 'add_motor': 'add_motor.png', 'tree': 'tree.png', 'gen': 'gen.png', 'home': 'home.png', 'form': 'form.png', 'edit': 'edit.png', 'delete': 'delete.png', 'statistics': 'statistics.png', 'reddot': 'reddot.png', 'orangedot': 'orangedot.png', 'greendot': 'greendot.png', 'bluedot': 'bluedot.png', 'purpledot': 'purpledot.png', 'yellowdot': 'yellowdot.png', 'maroondot': 'maroondot.png', 'send': 'send.png', 'zoomall': 'zoomall.png', 'connect': 'connect.png', 'continue': 'continue.png', 'alert': 'alert.png', 'gear': 'gear.png', 'generalsettings': 'generalsettings.png', 'open': 'open.png', 'loop': 'loop.png', 'save': 'save.png', 'stop': 'stop.png', 'restart': 'continue.png', 'res': 'res.png', 'sensor': 'sensor.png', 'start': 'start.png', 'motor': 'motor.png', 'pause': 'pause.png', 'pull': 'pull.png', 'push': 'push.png', 'list': 'list.png', 'disconnect': 'disconnect.png', 'configure': 'configure.png', 'pidconfig': 'pidconfig.png', 'consoletool': 'consoletool.png', 'fordward': 'fordward.png', 'fastbackward': 'fastbackward.png', 'backward': 'backward.png', 'stopjogging': 'stopjogging.png', 'fastfordward': 'fastfordward.png', 'customcommands':'customcommands.png' } currentDir = os.path.dirname(__file__) icon_path = os.path.join(currentDir, '../resources', file_index[icoName]) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(icon_path), QtGui.QIcon.Normal, QtGui.QIcon.Off) return icon class ConfigQLineEdit(QtWidgets.QLineEdit): return_key = 16777220 updateValue = QtCore.pyqtSignal() def __init__(self, parent=None): '''Constructor for ToolsWidget''' super().__init__(parent) def keyPressEvent(self, event): if event.key() == self.return_key: self.updateValue.emit() else: super().keyPressEvent(event) class WorkAreaTabWidget(QtWidgets.QTabWidget): def __init__(self, parent=None): '''Constructor for ToolsWidget''' super().__init__(parent) def getTabIcon(self): raise NotImplemented def getTabName(self): raise NotImplemented class SerialPortComboBox(QtWidgets.QComboBox): def __init__(self, parent=None, snifer=None): super().__init__(parent) self.addItems(self.getAvailableSerialPortNames()) def getAvailableSerialPortNames(self): portNames = [] for port in list_ports.comports(): if port[2] != 'n/a': portNames.append(port[0]) return portNames def showPopup(self): selectedItem = self.currentText() super().clear() availableSerialPortNames = self.getAvailableSerialPortNames() self.addItems(availableSerialPortNames) if selectedItem in availableSerialPortNames: self.setCurrentText(selectedItem) super().showPopup() ================================================ FILE: src/gui/toolbar.py ================================================ # !/usr/bin/env python # -*- coding: utf-8 -*- from PyQt5 import QtWidgets from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit class SimpleFOCConfigToolBar(QtWidgets.QToolBar): def __init__(self,main_window, devicesTabedWidget, parent=None): super().__init__(parent) self.addDeviceAction = QtWidgets.QToolButton(main_window) self.addDeviceAction.setIcon(GUIToolKit.getIconByName('add_motor')) self.addDeviceAction.setObjectName('addDeviceAction') self.addDeviceAction.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.addDeviceMenu = QtWidgets.QMenu(self.addDeviceAction) self.addDeviceTreeView = QtWidgets.QAction("Tree View",self.addDeviceMenu) self.addDeviceTreeView.setIcon(GUIToolKit.getIconByName('tree')) self.addDeviceTreeView.triggered.connect(devicesTabedWidget.addDeviceTree) self.addDeviceFormView = QtWidgets.QAction("Form View",self.addDeviceMenu) self.addDeviceFormView.setIcon(GUIToolKit.getIconByName('form')) self.addDeviceFormView.triggered.connect(devicesTabedWidget.addDeviceForm) self.addDeviceMenu.addAction(self.addDeviceTreeView) self.addDeviceMenu.addAction(self.addDeviceFormView) self.addDeviceAction.setMenu(self.addDeviceMenu) self.addWidget(self.addDeviceAction) self.openDeviceAction = QtWidgets.QAction(main_window) self.openDeviceAction.setIcon(GUIToolKit.getIconByName('open')) self.openDeviceAction.setObjectName('openDeviceAction') self.openDeviceAction.triggered.connect(devicesTabedWidget.openDevice) self.addAction(self.openDeviceAction) self.saveDeviceAction = QtWidgets.QAction(main_window) self.saveDeviceAction.setIcon(GUIToolKit.getIconByName('save')) self.saveDeviceAction.setObjectName('saveDeviceAction') self.saveDeviceAction.triggered.connect(devicesTabedWidget.saveDevice) self.addAction(self.saveDeviceAction) self.generateCodeAction = QtWidgets.QAction(main_window) self.generateCodeAction.setIcon(GUIToolKit.getIconByName('gen')) self.generateCodeAction.setObjectName('genertecode') self.generateCodeAction.triggered.connect(devicesTabedWidget.generateCode) self.addAction(self.generateCodeAction) self.addSeparator() self.openConsoleToolAction = QtWidgets.QAction(main_window) self.openConsoleToolAction.setIcon(GUIToolKit.getIconByName('consoletool')) self.openConsoleToolAction.setToolTip('Open Serial Cosole tool') self.openConsoleToolAction.setObjectName('openconsoletool') self.openConsoleToolAction.triggered.connect(devicesTabedWidget.openConsoleTool) self.addAction(self.openConsoleToolAction) self.addSeparator() ================================================ FILE: src/gui/workAreaTabbedWidget.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import json from PyQt5 import QtWidgets from src.gui.commandlinetool.commandlinetool import CommandLineConsoleTool from src.gui.configtool.deviceConfigurationTool import DeviceConfigurationTool from src.gui.configtool.generatedCodeDisplay import GeneratedCodeDisplay from src.gui.configtool.treeViewConfigTool import TreeViewConfigTool from src.simpleFOCConnector import SimpleFOCDevice class WorkAreaTabbedWidget(QtWidgets.QTabWidget): def __init__(self, parent=None): super().__init__(parent) self.setTabsClosable(True) self.setMovable(True) self.setObjectName('devicesTabWidget') self.device = SimpleFOCDevice.getInstance() self.cmdLineTool = None self.configDeviceTool = None self.generatedCodeTab = None self.activeToolsList = [] self.tabCloseRequested.connect(self.removeTabHandler) self.setStyleSheet( 'QTabBar::close - button { image: url(close.png) subcontrol - position: left; }') self.setStyleSheet('QTabBar::tab { height: 30px; width: 150px;}') def removeTabHandler(self, index): if type(self.currentWidget()) == CommandLineConsoleTool: self.cmdLineTool = None if type(self.currentWidget()) == DeviceConfigurationTool or type( self.currentWidget()) == TreeViewConfigTool: self.configDeviceTool = None if type(self.currentWidget()) == GeneratedCodeDisplay: self.generatedCodeTab = None if self.configDeviceTool == None and self.cmdLineTool == None: if self.device.isConnected: self.device.disConnect() self.activeToolsList.pop(index) self.removeTab(index) def addDeviceForm(self): if self.configDeviceTool is None: self.configDeviceTool = DeviceConfigurationTool() self.activeToolsList.append(self.configDeviceTool) self.addTab(self.configDeviceTool, self.configDeviceTool.getTabIcon(), 'Device') self.setCurrentIndex(self.currentIndex() + 1) def addDeviceTree(self): if self.configDeviceTool is None: self.configDeviceTool = TreeViewConfigTool() self.activeToolsList.append(self.configDeviceTool) self.addTab(self.configDeviceTool, self.configDeviceTool.getTabIcon(), 'Device') self.setCurrentIndex(self.currentIndex() + 1) def openDevice(self): if self.configDeviceTool is None: dlg = QtWidgets.QFileDialog() dlg.setFileMode(QtWidgets.QFileDialog.AnyFile) filenames = None if dlg.exec_(): filenames = dlg.selectedFiles() try: with open(filenames[0]) as json_file: configurationInfo = json.load(json_file) sfd = SimpleFOCDevice.getInstance() sfd.configureDevice(configurationInfo) self.configDeviceTool = TreeViewConfigTool() sfd.openedFile = filenames self.activeToolsList.append(self.configDeviceTool) tabName = self.configDeviceTool.getTabName() if tabName == '': tabName = 'Device' self.addTab(self.configDeviceTool, self.configDeviceTool.getTabIcon(), tabName) self.setCurrentIndex(self.currentIndex() + 1) except Exception as exception: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Warning) msgBox.setText('Error while opening selected file') msgBox.setWindowTitle('SimpleFOC configDeviceTool') msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec() def saveDevice(self): if len(self.activeToolsList) > 0: currentconfigDeviceTool = self.activeToolsList[self.currentIndex()] if currentconfigDeviceTool.device.openedFile is None: options = QtWidgets.QFileDialog.Options() options |= QtWidgets.QFileDialog.DontUseNativeDialog fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save device configuration', '', 'JSON configuration file (*.json)', options=options) if fileName: self.saveToFile(currentconfigDeviceTool.device, fileName) else: self.saveToFile(currentconfigDeviceTool.device, currentconfigDeviceTool.device.openedFile) def generateCode(self): if len(self.activeToolsList) > 0: currentconfigDeviceTool = self.activeToolsList[self.currentIndex()] self.generatedCodeTab = GeneratedCodeDisplay() self.activeToolsList.append(self.generatedCodeTab) self.addTab(self.generatedCodeTab, self.generatedCodeTab.getTabIcon(), self.generatedCodeTab.getTabName()) self.setCurrentIndex(self.currentIndex() + 1) def saveToFile(self, deviceToSave, file): if type(file) is list: with open(file[0], 'w', encoding='utf-8') as f: f.write(json.dumps(deviceToSave.toJSON(), indent=4, sort_keys=True)) else: with open(file, 'w', encoding='utf-8') as f: f.write(json.dumps(deviceToSave.toJSON(), indent=4, sort_keys=True)) def openConsoleTool(self): if self.cmdLineTool is None: self.cmdLineTool = CommandLineConsoleTool() self.activeToolsList.append(self.cmdLineTool) self.addTab(self.cmdLineTool, self.cmdLineTool.getTabIcon(), 'Cmd Line') self.setCurrentIndex(self.currentIndex() + 1) ================================================ FILE: src/simpleFOCConnector.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import logging import threading import time import serial from PyQt5 import QtCore, QtWidgets from serial import SerialException from collections import defaultdict class PIDController: P = 0 D = 0 I = 0 outputRamp = 0 outputLimit = 0 cmd ='' def __init__(self, cmd): self.cmd = cmd def load(self, jsonValues): self.P = jsonValues['P'] self.I = jsonValues['I'] self.D = jsonValues['D'] self.outputRamp = jsonValues['outputRamp'] self.outputLimit = jsonValues['outputLimit'] def serialize(self): return { 'P': self.P, 'I': self.I, 'D': self.D, 'outputRamp':self.outputRamp, 'outputLimit':self.outputLimit } class LowPassFilter: Tf = 0 cmd ='' cmdTf = 'F' def __init__(self, cmd): self.cmd = cmd class Command: cmdName = '' cmd = '' def __init__(self,cmdname='Command', cmd=''): self.cmdName = cmdname self.cmd = cmd def load(self, jsonValues): self.cmdName = jsonValues['commandName'] self.cmd = jsonValues['commandValue'] def serialize(self): return { 'commandName': self.cmdName, 'commandValue': self.cmd } class CustomCommands: customCommandsList = [] def __init__(self,commandsLis=[]): for command in commandsLis: self.customCommandsList.append(command) def load(self, jsonValues): for commandInJson in jsonValues: command = Command() command.load(commandInJson) self.customCommandsList.append(command) def serialize(self): serializedCustomCommands = defaultdict(list) for command in self.customCommandsList: serializedCommand = command.serialize() serializedCustomCommands['customCommands'].append(serializedCommand) return serializedCustomCommands class SimpleFOCDevice: __instance = None TORQUE_CONTROL = 0 VELOCITY_CONTROL = 1 ANGLE_CONTROL = 2 VELOCITY_OPENLOOP_CONTROL = 3 ANGLE_OPENLOOP_CONTROL = 4 SINE_PWM = 0 SPACE_VECTOR_PWM = 1 TRAPEZOIDAL_120 = 2 TRAPEZOIDAL_150 = 3 VOLTAGE_TORQUE = 0 DC_CURRENT_TORQUE = 1 FOC_CURRENT_TORQUE = 2 VELOCITY_PID = 'V' ANGLE_PID = 'A' CURRENT_Q_PID = 'Q' CURRENT_D_PID = 'D' PULL_CONFIG_ON_CONNECT = 'Pull config' PUSH_CONFG_ON_CONNECT = 'Push config' ONLY_CONNECT = 'Only connect' @staticmethod def getInstance(): """ Static access method. """ if SimpleFOCDevice.__instance == None: SimpleFOCDevice() return SimpleFOCDevice.__instance def __init__(self): """ Virtually private constructor. """ if SimpleFOCDevice.__instance != None: raise Exception("This class is a singleton!") else: # serial connection variables self.serialPort = None self.responseThread = None self.isConnected = False self.openedFile = None self.connectionStateListenerList = [] self.serialPortName = "" self.serialRate = 115200 self.serialByteSize = serial.EIGHTBITS self.serialParity = serial.PARITY_NONE self.stopBits = serial.STOPBITS_ONE self.commProvider = SerialPortReceiveHandler() self.commProvider.commandDataReceived.connect(self.parseResponses) self.commProvider.stateMonitorReceived.connect(self.parseStateResponses) self.connectionID = "" # command id of the device self.devCommandID = '' # motion control paramters self.PIDVelocity = PIDController(self.VELOCITY_PID) self.PIDAngle = PIDController(self.ANGLE_PID) self.PIDCurrentQ = PIDController(self.CURRENT_Q_PID) self.PIDCurrentD = PIDController(self.CURRENT_D_PID) self.LPFVelocity = LowPassFilter(self.VELOCITY_PID) self.LPFAngle = LowPassFilter(self.ANGLE_PID) self.LPFCurrentQ = LowPassFilter(self.CURRENT_Q_PID) self.LPFCurrentD = LowPassFilter(self.CURRENT_D_PID) self.velocityLimit = 0 self.voltageLimit = 0 self.currentLimit = 0 self.controlType = SimpleFOCDevice.ANGLE_CONTROL self.torqueType = SimpleFOCDevice.VOLTAGE_TORQUE self.initialTarget = 0 self.motionDownsample = 0 # monitor variables self.monitorDownsample = 0 self.monitorVariables = 0 # state variables self.target = 0 self.stateUpdater = StateUpdateRunner(self) self.targetNow = 0 self.angleNow = 0 self.velocityNow = 0 self.voltageQNow = 0 self.voltageDNow = 0 self.currentQNow = 0 self.currentDNow = 0 # general variables self.phaseResistance = 0 self.deviceStatus = 0 self.modulationType = 0 self.modulationCentered = 1 # sensor variables self.sensorElectricalZero = 0 self.sensorZeroOffset = 0 # list of custom commands self.customCommands = CustomCommands() # return the class instance SimpleFOCDevice.__instance = self def configureDevice(self, jsonValue): # motion control parameters self.PIDVelocity.load(jsonValue['PIDVelocity']) self.PIDAngle.load(jsonValue['PIDAngle']) self.PIDCurrentD.load(jsonValue['PIDCurrentD']) self.PIDCurrentQ.load(jsonValue['PIDCurrentQ']) # low pass filters self.LPFVelocity.Tf = jsonValue['LPFVelocity'] self.LPFAngle.Tf = jsonValue['LPFAngle'] self.LPFCurrentQ.Tf = jsonValue['LPFCurrentQ'] self.LPFCurrentD.Tf = jsonValue['LPFCurrentD'] # limit variables self.velocityLimit = jsonValue['velocityLimit'] self.voltageLimit = jsonValue['voltageLimit'] self.currentLimit = jsonValue['currentLimit'] # motion control types self.controlType = jsonValue['controlType'] self.torqueType = jsonValue['torqueType'] self.motionDownsample = jsonValue['motionDownsample'] # sensor zero offset and electrical zero offset self.sensorElectricalZero = jsonValue['sensorElectricalZero'] self.sensorZeroOffset = jsonValue['sensorZeroOffset'] # motor phase resistance self.phaseResistance = jsonValue['phaseResistance'] # initial target self.initialTarget = jsonValue['initialTarget'] # serial communication variables self.connectionID = jsonValue['connectionID'] self.serialPortName = jsonValue['serialPortName'] self.serialRate = jsonValue['serialRate'] self.serialByteSize = jsonValue['serialByteSize'] self.serialParity = jsonValue['serialParity'] self.stopBits = jsonValue['stopBits'] try: self.customCommands.customCommandsList = [] self.customCommands.load(jsonValue['customCommands']) except KeyError: pass try: self.devCommandID = jsonValue['devCommandID'] except KeyError: pass def configureConnection(self, configDict): self.connectionID = configDict['connectionID'] self.serialPortName = configDict['serialPortName'] self.serialRate = configDict['serialRate'] self.serialByteSize = configDict['serialByteSize'] self.serialParity = configDict['serialParity'] self.stopBits = configDict['stopBits'] def toJSON(self): valuesToSave = { 'PIDVelocity': self.PIDVelocity.serialize(), 'PIDAngle': self.PIDAngle.serialize(), 'PIDCurrentD': self.PIDCurrentD.serialize(), 'PIDCurrentQ': self.PIDCurrentQ.serialize(), 'LPFVelocity':self.LPFVelocity.Tf, 'LPFAngle':self.LPFAngle.Tf, 'LPFCurrentD':self.LPFCurrentD.Tf, 'LPFCurrentQ':self.LPFCurrentQ.Tf, 'velocityLimit': self.velocityLimit, 'voltageLimit': self.voltageLimit, 'currentLimit': self.currentLimit, 'controlType': self.controlType, 'motionDownsample':self.motionDownsample, 'torqueType': self.torqueType, 'phaseResistance': self.phaseResistance, 'sensorZeroOffset': self.sensorZeroOffset, 'sensorElectricalZero': self.sensorElectricalZero, 'initialTarget': self.initialTarget, 'connectionID': self.connectionID, 'serialPortName': self.serialPortName, 'serialRate': self.serialRate, 'serialByteSize': self.serialByteSize, 'serialParity': self.serialParity, 'stopBits': self.stopBits, 'devCommandID': self.devCommandID, } valuesToSave.update(self.customCommands.serialize()) return valuesToSave def toArduinoCode(self, generateParams = []): # code = "#include \n\n" # code += "void setup(){\n\n" # code += "....\n\n" code = "\n" if generateParams[0] or generateParams==[]: code += "// control loop type and torque mode \n" code += "motor.torque_controller = TorqueControlType::" if self.torqueType == self.VOLTAGE_TORQUE: code += "voltage" elif self.torqueType == self.DC_CURRENT_TORQUE: code += "dc_current" elif self.torqueType == self.FOC_CURRENT_TORQUE: code += "foc_current" code += ";\n" code += "motor.controller = MotionControlType::" if self.controlType == self.TORQUE_CONTROL: code += "torque" elif self.controlType == self.VELOCITY_CONTROL: code += "velocity" elif self.controlType == self.ANGLE_CONTROL: code += "angle" elif self.controlType == self.VELOCITY_CONTROL: code += "velocity_openloop" elif self.controlType == self.ANGLE_CONTROL: code += "angle_openloop" code += ";\n" code += "motor.motion_downsample = " + str(self.motionDownsample) +";\n" code += "\n" if generateParams[1] or generateParams==[]: code += "// velocity loop PID\n" code += "motor.PID_velocity.P = " + str(self.PIDVelocity.P) +";\n" code += "motor.PID_velocity.I = " + str(self.PIDVelocity.I) +";\n" code += "motor.PID_velocity.D = " + str(self.PIDVelocity.D) +";\n" code += "motor.PID_velocity.output_ramp = " + str(self.PIDVelocity.outputRamp) +";\n" code += "motor.PID_velocity.limit = " + str(self.PIDVelocity.outputLimit) +";\n" code += "// Low pass filtering time constant \n" code += "motor.LPF_velocity.Tf = " + str(self.LPFVelocity.Tf) +";\n" if generateParams[2] or generateParams==[]: code += "// angle loop PID\n" code += "motor.P_angle.P = " + str(self.PIDAngle.P) +";\n" code += "motor.P_angle.I = " + str(self.PIDAngle.I) +";\n" code += "motor.P_angle.D = " + str(self.PIDAngle.D) +";\n" code += "motor.P_angle.output_ramp = " + str(self.PIDAngle.outputRamp) +";\n" code += "motor.P_angle.limit = " + str(self.PIDAngle.outputLimit) +";\n" code += "// Low pass filtering time constant \n" code += "motor.LPF_angle.Tf = " + str(self.LPFAngle.Tf) +";\n" if generateParams[3] or generateParams==[]: code += "// current q loop PID \n" code += "motor.PID_current_q.P = " + str(self.PIDCurrentQ.P) +";\n" code += "motor.PID_current_q.I = " + str(self.PIDCurrentQ.I) +";\n" code += "motor.PID_current_q.D = " + str(self.PIDCurrentQ.D) +";\n" code += "motor.PID_current_q.output_ramp = " + str(self.PIDCurrentQ.outputRamp) +";\n" code += "motor.PID_current_q.limit = " + str(self.PIDCurrentQ.outputLimit) +";\n" code += "// Low pass filtering time constant \n" code += "motor.LPF_current_q.Tf = " + str(self.LPFCurrentQ.Tf) +";\n" if generateParams[4] or generateParams==[]: code += "// current d loop PID\n" code += "motor.PID_current_d.P = " + str(self.PIDCurrentD.P) +";\n" code += "motor.PID_current_d.I = " + str(self.PIDCurrentD.I) +";\n" code += "motor.PID_current_d.D = " + str(self.PIDCurrentD.D) +";\n" code += "motor.PID_current_d.output_ramp = " + str(self.PIDCurrentD.outputRamp) +";\n" code += "motor.PID_current_d.limit = " + str(self.PIDCurrentD.outputLimit) +";\n" code += "// Low pass filtering time constant \n" code += "motor.LPF_current_d.Tf = " + str(self.LPFCurrentD.Tf) +";\n" if generateParams[5] or generateParams==[]: code += "// Limits \n" code += "motor.velocity_limit = " + str(self.velocityLimit) +";\n" code += "motor.voltage_limit = " + str(self.voltageLimit) +";\n" code += "motor.current_limit = " + str(self.currentLimit) +";\n" if generateParams[6] or generateParams==[]: code += "// sensor zero offset - home position \n" code += "motor.sensor_offset = " + str(self.sensorZeroOffset) +";\n" if generateParams[7] or generateParams==[]: code += "// sensor zero electrical angle \n" code += "// this parameter enables skipping a part of initFOC \n" code += "motor.sensor_electrical_offset = " + str(self.sensorElectricalZero) +";\n" if generateParams[8] or generateParams==[]: code += "// general settings \n" code += "// motor phase resistance \n" code += "motor.phase_resistance = " + str(self.sensorElectricalZero) +";\n" if generateParams[9] or generateParams==[]: code += "// pwm modulation settings \n" code += "motor.foc_modulation = FOCModulationType::" if self.modulationType == self.SINE_PWM: code += "SinePWM" elif self.modulationType == self.SPACE_VECTOR_PWM: code += "SpaceVectorPWM" elif self.modulationType == self.TRAPEZOIDAL_120: code += "Trapezoid_120" elif self.modulationType == self.TRAPEZOIDAL_150: code += "Trapezoid_150" code += ";\n" code += "motor.modulation_centered = " + str(self.modulationCentered) +";\n" # code += "\n\nmotor.init();\nmotor.initFOC();\n\n...\n\n }" # code += "\n\nvoid loop() {\n\n....\n\n}" return code def __initCommunications(self): self.serialPort = serial.Serial(self.serialPortName, self.serialRate, self.serialByteSize, self.serialParity, self.stopBits) self.commProvider.serialComm = self.serialPort self.commProvider.start() def __closeCommunication(self): self.serialPort.close() def connect(self, connectionMode): try: self.__initCommunications() except SerialException as serEx: logging.warning('Is not possible to open serial port') logging.warning('Port =' + self.serialPortName) logging.warning('Rate =' + str(self.serialRate)) logging.warning('parity =' + str(self.serialParity)) logging.warning('Byte size =' + str(self.serialByteSize)) logging.warning('Stop bits=' + str(self.stopBits)) msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Warning) msgBox.setText('Error while trying to open serial port') msgBox.setWindowTitle('SimpleFOC ConfigTool') msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec() return False else: self.isConnected = True for listener in self.connectionStateListenerList: listener.connectionStateChanged(True) if connectionMode == SimpleFOCDevice.PULL_CONFIG_ON_CONNECT: self.pullConfiguration() if self.stateUpdater.stopped(): self.stateUpdater = StateUpdateRunner(self) self.stateUpdater.start() elif connectionMode == SimpleFOCDevice.PUSH_CONFG_ON_CONNECT: self.pushConfiguration() if self.stateUpdater.stopped(): self.stateUpdater = StateUpdateRunner(self) self.stateUpdater.start() pass return True def disConnect(self): self.isConnected = False self.__closeCommunication() self.stateUpdater.stop() for listener in self.connectionStateListenerList: listener.connectionStateChanged(False) def addConnectionStateListener(self, listener): self.connectionStateListenerList.append(listener) def sendCommand(self, command): if self.isConnected: self.serialPort.write((str(command) + '\n').encode('utf-8')) def setCommand(self, command, value): if self.isConnected: self.sendCommand(str(self.devCommandID) + str(command) + str(value)) def getCommand(self, command): if self.isConnected: self.sendCommand(str(self.devCommandID) + str(command) ) def sendControlType(self, loop_control_type): if self.isConnected: if loop_control_type != '': self.controlType = loop_control_type self.setCommand('C', str(loop_control_type)) def sendTorqueType(self, torque_type): if self.isConnected: if torque_type != '': self.torqueType = torque_type self.setCommand('T', str(torque_type)) def sendMotionDownsample(self, value): if self.isConnected: if value != '': self.motionDownsample = value self.setCommand('CD', str(value)) def sendProportionalGain(self, pid, value ): if self.isConnected: if value != '': pid.P = value self.setCommand(str(pid.cmd)+'P', str(value)) def sendIntegralGain(self, pid, value): if self.isConnected: if value != '': pid.I = value self.setCommand(str(pid.cmd)+'I', str(value)) def sendDerivativeGain(self, pid, value): if self.isConnected: if value != '': pid.D = value self.setCommand(str(pid.cmd)+'D', str(value)) def sendOutputRamp(self, pid, value): if self.isConnected: if value != '': pid.outputRamp = value self.setCommand(str(pid.cmd)+'R', str(value)) def sendOutputLimit(self, pid, value): if self.isConnected: if value != '': pid.outputLimit = value self.setCommand(str(pid.cmd)+'L', str(value)) def sendLowPassFilter(self, lpf, value): if self.isConnected: if value != '': lpf.Tf = value self.setCommand(str(lpf.cmd)+'F', str(value)) def sendVelocityLimit(self, value): if self.isConnected: if value != '': self.velocityLimit = value self.setCommand('LV', str(value)) def sendVoltageLimit(self, value): if self.isConnected: if value != '': self.voltageLimit = value self.setCommand('LU', str(value)) def sendCurrentLimit(self, value): if self.isConnected: if value != '': self.currentLimit = value self.setCommand('LC', str(value)) def sendPhaseResistance(self, value): if self.isConnected: if value != '': self.phaseResistance = value self.setCommand('R', str(value)) def sendTargetValue(self, targetvalue): if self.isConnected: if targetvalue != '': self.target = targetvalue self.setCommand('',self.target) def sendSensorZeroOffset(self, targetvalue): if self.isConnected: if targetvalue != '': self.sensorZeroOffset = targetvalue self.setCommand('SM', str(targetvalue)) def sendSensorZeroElectrical(self, targetvalue): if self.isConnected: if targetvalue != '': self.sensorElectricalZero = targetvalue self.setCommand('SE', str(targetvalue)) def sendDeviceStatus(self, targetvalue): if self.isConnected: if targetvalue != '': self.deviceStatus = targetvalue self.setCommand('E', str(targetvalue)) def sendModulationCentered(self, targetvalue): if self.isConnected: if targetvalue != '': self.modulationCentered = targetvalue self.setCommand('WC', str(targetvalue)) def sendModulationType(self, targetvalue): if self.isConnected: if targetvalue != '': self.modulationType = targetvalue self.setCommand('WT', str(targetvalue)) def sendDeviceStatus(self, targetvalue): if self.isConnected: if targetvalue != '': self.deviceStatus = targetvalue self.setCommand('E', str(targetvalue)) def sendMonitorDownsample(self, targetvalue): if self.isConnected: if targetvalue != '': self.monitorDownsample = targetvalue self.setCommand('MD', str(targetvalue)) def sendMonitorClearVariables(self): if self.isConnected: self.monitorVariables = 0 self.getCommand('MC') def sendMonitorVariables(self, vararray): if self.isConnected: if vararray != '': val = 0 m = 10**6 for var in vararray: val = val+ int(var)*m m = m/10 self.monitorVariables = vararray self.setCommand('MS', "{:07d}".format(int(val))) else: self.getCommand('MS') def updateStates(self): if self.isConnected: self.getCommand('MG0') time.sleep(100 / 1000) self.getCommand('MG1') time.sleep(100 / 1000) self.getCommand('MG2') time.sleep(100 / 1000) self.getCommand('MG3') time.sleep(100 / 1000) self.getCommand('MG4') time.sleep(100 / 1000) self.getCommand('MG5') time.sleep(100 / 1000) self.getCommand('MG6') time.sleep(100 / 1000) def pushConfiguration(self): print("push") # self.sendControlType(self.controlType) # self.sendProportionalGain(self.PIDVelocity, self.self) # self.sendIntegralGain(self.PIDVelocity, self.integralGainPID) # self.sendDerivativeGain(self.PIDVelocity, self.derivativeGainPID) # self.sendOutputRamp(self.PIDVelocity, self.voltageRampPID) # self.sendLowPassFilter(self.LPFVelocity,self.lowPassFilter) # self.sendPGain(self.anglePGain) # self.sendVelocityLimit(self.velocityLimit) # self.sendVoltageLimit(self.voltageLimit) # self.sendTargetValue(self.initialTarget) def pullPIDConf(self, pid, lpf): self.sendProportionalGain(pid,'') time.sleep(5 / 1000) self.sendIntegralGain(pid,'') time.sleep(5 / 1000) self.sendDerivativeGain(pid,'') time.sleep(5 / 1000) self.sendOutputRamp(pid,'') time.sleep(5 / 1000) self.sendOutputLimit(pid,'') time.sleep(5 / 1000) self.sendLowPassFilter(lpf,'') def pullConfiguration(self): time.sleep(5 / 1000) self.sendControlType('') time.sleep(5 / 1000) self.sendTorqueType('') time.sleep(5 / 1000) self.pullPIDConf( self.PIDVelocity, self.LPFVelocity) time.sleep(5 / 1000) self.pullPIDConf( self.PIDAngle, self.LPFAngle) time.sleep(5 / 1000) self.pullPIDConf( self.PIDCurrentD, self.LPFCurrentD) time.sleep(5 / 1000) self.pullPIDConf( self.PIDCurrentQ, self.LPFCurrentQ) time.sleep(5 / 1000) self.sendVelocityLimit('') time.sleep(5 / 1000) self.sendVoltageLimit('') time.sleep(5 / 1000) self.sendCurrentLimit('') time.sleep(5 / 1000) self.sendSensorZeroElectrical('') time.sleep(5 / 1000) self.sendSensorZeroOffset('') time.sleep(5 / 1000) self.sendMotionDownsample('') time.sleep(5 / 1000) self.sendPhaseResistance('') time.sleep(5 / 1000) self.sendModulationCentered('') time.sleep(5 / 1000) self.sendModulationCentered('') time.sleep(5 / 1000) self.sendDeviceStatus('') def parsePIDFResponse(self, pid, lpf, comandResponse): if 'P' in comandResponse: pid.P = float(comandResponse.replace('P: ', '')) if 'I' in comandResponse: pid.I = float(comandResponse.replace('I: ', '')) if 'D' in comandResponse: pid.D = float(comandResponse.replace('D: ', '')) if 'ramp' in comandResponse: val = comandResponse.replace('ramp:', '') if 'ovf' in val: pid.outputRamp = 0 else: pid.outputRamp = float(comandResponse.replace('ramp:', '')) if 'limit' in comandResponse: pid.outputLimit = float(comandResponse.replace('limit:', '')) if 'Tf' in comandResponse: lpf.Tf = float(comandResponse.replace('Tf: ', '')) def parseLimitsResponse(self, comandResponse): if 'vel:' in comandResponse: self.velocityLimit = float(comandResponse.replace('vel:', '')) elif 'volt:' in comandResponse: self.voltageLimit = float(comandResponse.replace('volt:', '')) elif 'curr:' in comandResponse: self.currentLimit = float(comandResponse.replace('curr:', '')) def parseMotionResponse(self, comandResponse): if 'downsample' in comandResponse: self.motionDownsample = float(comandResponse.replace('downsample:', '')) elif 'torque' in comandResponse: self.controlType = 0 elif 'angle open' in comandResponse: self.controlType = 4 elif 'angle' in comandResponse: self.controlType = 2 elif 'vel open' in comandResponse: self.controlType = 3 elif 'vel' in comandResponse: self.controlType = 1 def parsePWMModResponse(self, comandResponse): if 'center' in comandResponse: self.modulationCentered = float(comandResponse.replace('center:', '')) elif 'type' in comandResponse: comandResponse = comandResponse.replace('type:', '') if 'Sine' in comandResponse: self.modulationType = self.SINE_PWM elif 'SVPWM' in comandResponse: self.modulationType = self.SPACE_VECTOR_PWM elif 'Trap 120' in comandResponse: self.modulationType = self.TRAPEZOIDAL_120 elif 'Trap 150' in comandResponse: self.modulationType = self.TRAPEZOIDAL_150 def parseTorqueResponse(self, comandResponse): if 'volt' in comandResponse: self.torqueType = 0 elif 'dc curr' in comandResponse: self.torqueType = 1 elif 'foc curr' in comandResponse: self.torqueType = 2 def parseSensorResponse(self, comandResponse): if 'el. offset' in comandResponse: self.sensorElectricalZero = float(comandResponse.replace('el. offset:', '')) elif 'offset' in comandResponse: self.sensorZeroOffset = float(comandResponse.replace('offset:', '')) def parseMonitorResponse(self, comandResponse): if 'all' in comandResponse: varStr = comandResponse.replace('all:', '') states = varStr.rstrip().split('\t', 7) self.targetNow = states[0] self.voltageQNow = states[1] self.voltageDNow = states[2] self.currentQNow = states[3] self.currentDNow = states[4] self.velocityNow = states[5] self.angleNow = states[6] if 'target' in comandResponse: self.targetNow = float(comandResponse.replace('target:', '')) elif 'Vq' in comandResponse: self.voltageQNow = float(comandResponse.replace('Vq:', '')) elif 'Vd' in comandResponse: self.voltageDNow = float(comandResponse.replace('Vd:', '')) elif 'Cq' in comandResponse: self.currentQNow = float(comandResponse.replace('Cq:', '')) elif 'Cd' in comandResponse: self.currentDNow = float(comandResponse.replace('Cd:', '')) elif 'vel' in comandResponse: self.velocityNow = float(comandResponse.replace('vel:', '')) elif 'angle' in comandResponse: self.angleNow = float(comandResponse.replace('angle:', '')) def parseResponses(self, comandResponse): if 'PID vel' in comandResponse: comandResponse = comandResponse.replace('PID vel|', '') self.parsePIDFResponse(self.PIDVelocity, self.LPFVelocity, comandResponse) elif 'PID angle' in comandResponse: comandResponse = comandResponse.replace('PID angle|', '') self.parsePIDFResponse(self.PIDAngle, self.LPFAngle, comandResponse) elif 'PID curr q' in comandResponse: comandResponse = comandResponse.replace('PID curr q|', '') self.parsePIDFResponse(self.PIDCurrentQ, self.LPFCurrentQ, comandResponse) elif 'PID curr d' in comandResponse: comandResponse = comandResponse.replace('PID curr d|', '') self.parsePIDFResponse(self.PIDCurrentD, self.LPFCurrentD, comandResponse) elif 'Limits' in comandResponse: comandResponse = comandResponse.replace('Limits|', '') self.parseLimitsResponse(comandResponse) elif 'Motion' in comandResponse: comandResponse = comandResponse.replace('Motion:', '') self.parseMotionResponse(comandResponse) elif 'Torque' in comandResponse: comandResponse = comandResponse.replace('Torque:', '') self.parseTorqueResponse(comandResponse) elif 'Sensor' in comandResponse: comandResponse = comandResponse.replace('Sensor |', '') self.parseSensorResponse(comandResponse) elif 'Monitor' in comandResponse: comandResponse = comandResponse.replace('Monitor |', '') self.parseMonitorResponse(comandResponse) elif 'Status' in comandResponse: self.deviceStatus = float(comandResponse.replace('Status:', '')) elif 'R phase' in comandResponse: self.phaseResistance = float(comandResponse.replace('R phase:', '')) elif 'PWM Mod' in comandResponse: comandResponse = comandResponse.replace('PWM Mod | ', '') self.parsePWMModResponse(comandResponse) def parseStateResponses(self, comandResponse): if 'Monitor' in comandResponse: comandResponse = comandResponse.replace('Monitor |', '') self.parseMonitorResponse(comandResponse) class SerialPortReceiveHandler(QtCore.QThread): monitoringDataReceived = QtCore.pyqtSignal(list) commandDataReceived = QtCore.pyqtSignal(str) stateMonitorReceived = QtCore.pyqtSignal(str) rawDataReceived = QtCore.pyqtSignal(str) def __init__(self, serial_port=None, *args,**kwargs): super(SerialPortReceiveHandler, self).__init__(*args, **kwargs) self._stop_event = threading.Event() self.serialComm = serial_port def handle_received_data(self, data): if not data: return if self.isDataReceivedMonitoring(data): try: v = data.rstrip().split('\t') self.monitoringDataReceived.emit(v) except ValueError as error: logging.error(error, exc_info=True) logging.error('data =' + str(data), exc_info=True) except IndexError as error: logging.error(error, exc_info=True) logging.error('data =' + str(data), exc_info=True) elif self.isDataReceivedStates(data): self.stateMonitorReceived.emit(data.rstrip()) else: self.commandDataReceived.emit(data.rstrip()) self.rawDataReceived.emit(data.rstrip()) def isDataReceivedMonitoring(self, data): if data[0].isdigit() or data[0] == '-': return True else: return False def isDataReceivedStates(self, data): if 'Monitor' in data: return True else: return False def run(self): try: while not self.stopped(): if self.serialComm is not None: if self.serialComm.isOpen(): reading = self.serialComm.readline() if reading: self.handle_received_data(reading.decode()) except SerialException as serialException: logging.error(serialException, exc_info=True) except TypeError as typeError: logging.error(typeError, exc_info=True) except AttributeError as ae: logging.error(ae, exc_info=True) def stop(self): self._stop_event.set() def stopped(self): return self._stop_event.is_set() class StateUpdateRunner(QtCore.QThread): def __init__(self, connector=None, *args,**kwargs): super(StateUpdateRunner, self).__init__(*args, **kwargs) self._stop_event = threading.Event() self.deviceConnector = connector def run(self): try: while not self.stopped(): if self.deviceConnector is not None: if self.deviceConnector.commProvider.serialComm.isOpen(): self.deviceConnector.updateStates() time.sleep(1) except SerialException as serialException: logging.error(serialException, exc_info=True) def stop(self): self._stop_event.set() def stopped(self): return self._stop_event.is_set()