├── .gitignore
├── DOC
├── customCommands.png
├── formview.png
├── gen.gif
├── gen.png
├── new_gif.gif
├── term.png
├── treeview.png
└── treeview_zh.png
├── LICENSE
├── README.md
├── SimpleFOCStudio.spec
├── device.json
├── requirements.txt
├── scriptTest.py
├── simpleFOCStudio.py
└── 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.png
│ ├── add_motor.png
│ ├── add_motor.psd
│ ├── alert.png
│ ├── ard.png
│ ├── backward.png
│ ├── bluedot.png
│ ├── configure.png
│ ├── connect.png
│ ├── consoletool.png
│ ├── continue.png
│ ├── customcommands.png
│ ├── delete.png
│ ├── disconnect.png
│ ├── edit.png
│ ├── fastbackward.png
│ ├── fastfordward.png
│ ├── fordward.png
│ ├── form.png
│ ├── gear.png
│ ├── gen.png
│ ├── generalsettings.png
│ ├── greendot.png
│ ├── home.png
│ ├── list.png
│ ├── loop.png
│ ├── maroondot.png
│ ├── motor.png
│ ├── motor.psd
│ ├── motor1.png
│ ├── open.png
│ ├── orangedot.png
│ ├── pause.png
│ ├── pid.png
│ ├── pidconfig.png
│ ├── pull.png
│ ├── purpledot.png
│ ├── push.png
│ ├── reddot.png
│ ├── res.png
│ ├── save.png
│ ├── send.png
│ ├── sensor.png
│ ├── start.png
│ ├── statistics.png
│ ├── stop.png
│ ├── stopjogging.png
│ ├── studioicon.icns
│ ├── tree copy.png
│ ├── tree.png
│ ├── yellowdot.png
│ └── zoomall.png
├── sharedcomnponets
│ ├── commandLineInterface.py
│ └── sharedcomponets.py
├── toolbar.py
└── workAreaTabbedWidget.py
└── simpleFOCConnector.py
/.gitignore:
--------------------------------------------------------------------------------
1 | /venv
2 | /.idea
3 | **/*.log
4 | **/__pycache__
--------------------------------------------------------------------------------
/DOC/customCommands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/customCommands.png
--------------------------------------------------------------------------------
/DOC/formview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/formview.png
--------------------------------------------------------------------------------
/DOC/gen.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/gen.gif
--------------------------------------------------------------------------------
/DOC/gen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/gen.png
--------------------------------------------------------------------------------
/DOC/new_gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/new_gif.gif
--------------------------------------------------------------------------------
/DOC/term.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/term.png
--------------------------------------------------------------------------------
/DOC/treeview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/treeview.png
--------------------------------------------------------------------------------
/DOC/treeview_zh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/DOC/treeview_zh.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 JorgeMaker
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## *Simple**FOC**Studio*
2 |
3 |
4 |
5 |
6 | Forked from [**SimpleFOCStudio**](https://github.com/JorgeMaker/SimpleFOCStudio), commit [0e460b6](https://github.com/JorgeMaker/SimpleFOCStudio/commit/0e460b665964308d3e6dba8d61d5473a58c26b2b).
7 |
8 | 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.
9 | #### The main features are:
10 | - Plug and play with the *Simple**FOC**library* version 2.1
11 | - Real-time tuning and configuration of the motors
12 | - Real-time plotting and monitoring of motor variables
13 | - Code generation for easier integration of the tuned parameters in your code
14 | - Built on PyQt5 and a standardized `SimpleFOCConnector` interface that can be used as a gateway form python to the *Simple**FOC**library* device.
15 |
16 |
17 |
18 |
19 |
20 | ### Installation
21 | Don't worry, *Simple**FOC**Studio* is easy to install even if you have never used the terminal before! 😃
22 | There are just couple of steps to take:
23 | 1. Install Python if you don't have it installed yet
24 | - We suggest to use Anaconda. [Here is how to install it.](https://docs.anaconda.com/anaconda/install/)
25 | - Once you have your Anaconda running open your terminal (on windows anaconda prompt) and run:
26 | ```sh
27 | conda create -n simplefoc python=3.6.0
28 | ```
29 | - Once this is done you will never have to run that command again, from now on you will just need:
30 | ```sh
31 | conda activate simplefoc
32 | ```
33 | 2. Clone this repository or download the zip file
34 | 3. Enter the folder containing the repository using the terminal
35 | - the command will be something like this:
36 | ```sh
37 | cd some_path_on_disk/SimpleFOCStudio
38 | ```
39 | 4. Final step of the installation is installing all the necessary libraries for the *Simple**FOC**Studio* :
40 | ```sh
41 | pip install -r "requirements.txt"
42 | ```
43 |
44 | 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:
45 | ```sh
46 | python simpleFOCStudio.py
47 | ```
48 | Or if using Anaconda:
49 | ```sh
50 | conda activate simplefoc
51 | python simpleFOCStudio.py
52 | ```
53 |
54 |
55 | ### Usage
56 | *Simple**FOC**Studio* has several useful features:
57 | - A simple approach to tuning your motor setup
58 | - Form view for fast motion control PID/LPF tuning
59 | - TreeView for more in depth tunning and experimenting
60 | - Code generation for transferring the found parameters into your arduino code
61 | - Serial terminal integrated with various commander features
62 | #### Motion control tunning windows
63 |
64 |
65 | 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.
66 | - To connect to your device first configure the serial port by clicking on
Configure button
67 | - Add your com port info and click OK
68 | - Then add the device command ID that you've added to the commander usually its `M`
69 | - Command `M` , Arduino code : `command.add('M',doMotor,"my motor")`
70 | - Command `A` , Arduino code : `command.add('A',doMotor,"my motor")`
71 | - Then click to the
Connect button and you should be ready to go!
72 |
73 |
74 |
75 |
76 |
77 |
78 | #### Code generation
79 |
80 | *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 :
81 | - Click on the
Arudino button in the toolbar.
82 | - Choose which sets of parameters you wish to generate the code for and click OK
83 | - In the new tab you will have a code of your tuned parameters.
84 |
85 | The generated code you can just copy/paste in your setup()
function, just before calling the motor.init()
86 |
87 |
88 |
89 |
90 |
91 | #### Custom Commands
92 |
93 | 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.
94 |
95 |
96 |
97 |
98 | 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 (→).
99 |
100 | #### Integrated serial terminal
101 |
102 | *Simple**FOC**Studio* also has integrated serial terminal for easier debugging and monitoring.
103 |
104 |
105 |
106 |
107 |
108 | ### Arduino code
109 | Basically there are two things you need to do:
110 | 1. Use the commander interface and add the motor to the commander
111 | 2. Use the monitoring and add the `motor.monitor()` in the loop
112 |
113 | Here is a mockup of the code:
114 |
115 | ```cpp
116 | #include
117 |
118 | ....
119 |
120 | // include commander interface
121 | Commander command = Commander(Serial);
122 | void doMotor(char* cmd) { command.motor(&motor, cmd); }
123 |
124 | void setup(){
125 | ....
126 | // add the motor to the commander interface
127 | // The letter (here 'M') you will provide to the SimpleFOCStudio
128 | command.add('M',doMotor,'motor');
129 | // tell the motor to use the monitoring
130 | motor.useMonitoring(Serial);
131 | motor.monitor_downsample = 0; // disable monitor at first - optional
132 | ...
133 |
134 | }
135 | void loop(){
136 | ....
137 |
138 | ....
139 | // real-time monitoring calls
140 | motor.monitor();
141 | // real-time commander calls
142 | command.run();
143 | }
144 | ```
145 |
--------------------------------------------------------------------------------
/SimpleFOCStudio.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 | block_cipher = None
4 |
5 | a = Analysis(['simpleFOCStudio.py'],
6 | pathex=['/Users/Jorge/Documents/PycharmProjects/SimpleFOCStudioUpdate'],
7 | win_no_prefer_redirects=False,
8 | win_private_assemblies=False,
9 | cipher=block_cipher,
10 | noarchive=False)
11 | pyz = PYZ(a.pure, a.zipped_data,
12 | cipher=block_cipher)
13 | a.datas += [('src/gui/resources/add.png', 'src/gui/resources/add.png', 'DATA'),]
14 | a.datas += [('src/gui/resources/alert.png', 'src/gui/resources/alert.png', 'DATA'),]
15 | a.datas += [('src/gui/resources/bluedot.png', 'src/gui/resources/bluedot.png', 'DATA'),]
16 | a.datas += [('src/gui/resources/configure.png', 'src/gui/resources/configure.png', 'DATA'),]
17 | a.datas += [('src/gui/resources/connect.png', 'src/gui/resources/connect.png', 'DATA'),]
18 | a.datas += [('src/gui/resources/continue.png', 'src/gui/resources/continue.png', 'DATA'),]
19 | a.datas += [('src/gui/resources/delete.png', 'src/gui/resources/delete.png', 'DATA'),]
20 | a.datas += [('src/gui/resources/disconnect.png', 'src/gui/resources/disconnect.png', 'DATA'),]
21 | a.datas += [('src/gui/resources/edit.png', 'src/gui/resources/edit.png', 'DATA'),]
22 | a.datas += [('src/gui/resources/greendot.png', 'src/gui/resources/greendot.png', 'DATA'),]
23 | a.datas += [('src/gui/resources/motor.png', 'src/gui/resources/motor.png', 'DATA'),]
24 | a.datas += [('src/gui/resources/open.png', 'src/gui/resources/open.png', 'DATA'),]
25 | a.datas += [('src/gui/resources/pause.png', 'src/gui/resources/pause.png', 'DATA'),]
26 | a.datas += [('src/gui/resources/purpledot.png', 'src/gui/resources/purpledot.png', 'DATA'),]
27 | a.datas += [('src/gui/resources/reddot.png', 'src/gui/resources/reddot.png', 'DATA'),]
28 | a.datas += [('src/gui/resources/save.png', 'src/gui/resources/save.png', 'DATA'),]
29 | a.datas += [('src/gui/resources/send.png', 'src/gui/resources/send.png', 'DATA'),]
30 | a.datas += [('src/gui/resources/start.png', 'src/gui/resources/start.png', 'DATA'),]
31 | a.datas += [('src/gui/resources/statistics.png', 'src/gui/resources/statistics.png', 'DATA'),]
32 | a.datas += [('src/gui/resources/stop.png', 'src/gui/resources/stop.png', 'DATA'),]
33 | a.datas += [('src/gui/resources/zoomall.png', 'src/gui/resources/zoomall.png', 'DATA'),]
34 | a.datas += [('src/gui/resources/consoletool.png', 'src/gui/resources/consoletool.png', 'DATA'),]
35 | exe = EXE(pyz,
36 | a.scripts,
37 | a.binaries,
38 | a.zipfiles,
39 | a.datas,
40 | [],
41 | name='SimpleFOCStudio',
42 | debug=False,
43 | bootloader_ignore_signals=False,
44 | strip=False,
45 | upx=True,
46 | upx_exclude=[],
47 | runtime_tmpdir=None,
48 | console=True,
49 | windowed=True)
50 | app = BUNDLE(exe,
51 | name='SimpleFOCStudio.app',
52 | icon='src/gui/resources/studioicon.icns',
53 | bundle_identifier=None)
54 |
--------------------------------------------------------------------------------
/device.json:
--------------------------------------------------------------------------------
1 | {
2 | "LPFAngle": 0.0,
3 | "LPFCurrentD": 0.005,
4 | "LPFCurrentQ": 0.005,
5 | "LPFVelocity": 0.2,
6 | "PIDAngle": {
7 | "D": 0.0,
8 | "I": 0.0,
9 | "P": 20.0,
10 | "outputLimit": 10.0,
11 | "outputRamp": 0.0
12 | },
13 | "PIDCurrentD": {
14 | "D": 0.0,
15 | "I": 300.0,
16 | "P": 3.0,
17 | "outputLimit": 12.0,
18 | "outputRamp": 0.0
19 | },
20 | "PIDCurrentQ": {
21 | "D": 0.0,
22 | "I": 300.0,
23 | "P": 3.0,
24 | "outputLimit": 12.0,
25 | "outputRamp": 0.0
26 | },
27 | "PIDVelocity": {
28 | "D": 0.0,
29 | "I": 20.0,
30 | "P": 0.2,
31 | "outputLimit": 11.0,
32 | "outputRamp": 1000.0
33 | },
34 | "connectionID": "",
35 | "controlType": 2,
36 | "currentLimit": 0.2,
37 | "customCommands": [
38 | {
39 | "commandName": "Command1",
40 | "commandValue": "D100"
41 | }
42 | ],
43 | "devCommandID": "M",
44 | "initialTarget": 0,
45 | "motionDownsample": 0.0,
46 | "phaseResistance": 0.0,
47 | "sensorElectricalZero": 2.676,
48 | "sensorZeroOffset": 0.0,
49 | "serialByteSize": 8,
50 | "serialParity": "N",
51 | "serialPortName": "/dev/cu.usbserial-14140",
52 | "serialRate": "115200",
53 | "stopBits": 1,
54 | "torqueType": 0,
55 | "velocityLimit": 10.0,
56 | "voltageLimit": 11.0
57 | }
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyQt5==5.15.2
2 | pyqtgraph==0.11.1
3 | pyserial==3.5
4 | numpy
--------------------------------------------------------------------------------
/scriptTest.py:
--------------------------------------------------------------------------------
1 | from src.simpleFOCConnector import SimpleFOCDevice
2 | import time
3 | if __name__ == '__main__':
4 | deviceConnector = SimpleFOCDevice.getInstance()
5 | deviceConnector.serialPortName = 'COM32'
6 | deviceConnector.serialRate = 115200
7 | deviceConnector.devCommandID = 'M'
8 |
9 | if deviceConnector.connect(SimpleFOCDevice.ONLY_CONNECT):
10 | while True:
11 | deviceConnector.sendControlType(SimpleFOCDevice.ANGLE_CONTROL)
12 | deviceConnector.sendTargetValue(0)
13 | time.sleep(2.0)
14 | deviceConnector.sendTargetValue(3)
15 | time.sleep(2)
16 | deviceConnector.sendTargetValue(6)
17 | time.sleep(2)
18 | deviceConnector.sendTargetValue(-6)
19 | time.sleep(2)
20 | deviceConnector.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL)
21 | deviceConnector.sendTargetValue(30)
22 | time.sleep(2)
23 | deviceConnector.sendTargetValue(-30)
24 | time.sleep(2)
25 |
--------------------------------------------------------------------------------
/simpleFOCStudio.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """ This module contains ans script to start the SimpleFOC ConfigTool, a GIU
4 | application ta monitor, tune and configure BLDC motor controllers based on
5 | SimpleFOC library.
6 | """
7 | from PyQt5 import QtWidgets
8 | from src.gui.mainWindow import UserInteractionMainWindow
9 | import sys
10 | import logging
11 |
12 | if __name__ == '__main__':
13 | try:
14 | logging.basicConfig(filename='.SimpleFOCConfigTool.log', filemode='w',
15 | format='%(name)s - %(levelname)s - %(message)s')
16 | app = QtWidgets.QApplication(sys.argv)
17 | mainWindow = QtWidgets.QMainWindow()
18 | userInteractionMainWindow = UserInteractionMainWindow()
19 | userInteractionMainWindow.setupUi(mainWindow)
20 | mainWindow.show()
21 | sys.exit(app.exec_())
22 | except Exception as exception:\
23 | logging.error(exception, exc_info=True)
24 |
--------------------------------------------------------------------------------
/src/gui/commandlinetool/commandlinetool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 |
5 | from src.gui.commandlinetool.configureConnectionWidget import \
6 | ConfigureConnection
7 | from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget
8 | from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget,
9 | GUIToolKit)
10 | from src.simpleFOCConnector import SimpleFOCDevice
11 |
12 |
13 | class CommandLineConsoleTool(WorkAreaTabWidget):
14 |
15 | def __init__(self, parent=None):
16 | super().__init__(parent)
17 |
18 | self.device = SimpleFOCDevice.getInstance()
19 |
20 | self.verticalLayout = QtWidgets.QVBoxLayout(self)
21 | self.verticalLayout.setObjectName('verticalLayout')
22 |
23 | self.configureConnection = ConfigureConnection()
24 | self.verticalLayout.addWidget(self.configureConnection)
25 |
26 | self.commandLineInterface = CommandLineWidget()
27 | self.verticalLayout.addWidget(self.commandLineInterface)
28 |
29 | self.device.commProvider.rawDataReceived.connect(self.commandLineInterface.publishCommandResponseData)
30 |
31 | def getTabIcon(self):
32 | return GUIToolKit.getIconByName('consoletool')
33 |
34 | def getTabName(self):
35 | return self.device.connectionID
36 |
--------------------------------------------------------------------------------
/src/gui/commandlinetool/configureConnectionWidget.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import serial
4 | from PyQt5 import (QtGui, QtCore, QtWidgets)
5 |
6 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
7 | from src.gui.sharedcomnponets.sharedcomponets import SerialPortComboBox
8 | from src.simpleFOCConnector import SimpleFOCDevice
9 |
10 |
11 | class ConfigureConnection(QtWidgets.QGroupBox):
12 |
13 | def __init__(self, parent=None):
14 | super().__init__(parent)
15 |
16 | self.device = SimpleFOCDevice.getInstance()
17 |
18 | self.setTitle('串口连接设置')
19 | self.setObjectName('configureConnection')
20 |
21 | self.configCoonLayout = QtWidgets.QHBoxLayout(self)
22 | self.configCoonLayout.setObjectName(
23 | 'configureConnectionorizontalLayout')
24 |
25 | self.portNameLabel = QtWidgets.QLabel(self)
26 | self.portNameLabel.setObjectName('portNameLabel')
27 | self.configCoonLayout.addWidget(self.portNameLabel)
28 |
29 | self.portNameComboBox = SerialPortComboBox(self)
30 | self.portNameComboBox.setObjectName('portNameComboBox')
31 | self.portNameComboBox.setMinimumWidth(250)
32 | self.configCoonLayout.addWidget(self.portNameComboBox)
33 |
34 | self.bitRateLabel = QtWidgets.QLabel(self)
35 | self.bitRateLabel.setObjectName('bitRateLabel')
36 | self.configCoonLayout.addWidget(self.bitRateLabel)
37 |
38 | self.bitRatelineEdit = QtWidgets.QLineEdit(self)
39 | self.bitRatelineEdit.setObjectName('bitRatelineEdit')
40 | self.bitRatelineEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp("^[0-9]*$")))
41 | self.bitRatelineEdit.setText('115200')
42 | self.configCoonLayout.addWidget(self.bitRatelineEdit)
43 |
44 | self.parityLabel = QtWidgets.QLabel(self)
45 | self.parityLabel.setObjectName('parityLabel')
46 | self.configCoonLayout.addWidget(self.parityLabel)
47 |
48 | self.parityComboBox = QtWidgets.QComboBox(self)
49 | self.parityComboBox.setObjectName('parityComboBox')
50 | self.parityComboBox.addItems(serial.PARITY_NAMES.values())
51 | self.configCoonLayout.addWidget(self.parityComboBox)
52 |
53 | serial.PARITY_NAMES.values()
54 |
55 | self.byteSizeLabel = QtWidgets.QLabel(self)
56 | self.byteSizeLabel.setObjectName('byteSizeLabel')
57 | self.configCoonLayout.addWidget(self.byteSizeLabel)
58 |
59 | self.byteSizeComboBox = QtWidgets.QComboBox(self)
60 | self.byteSizeComboBox.setObjectName('byteSizeComboBox')
61 | byteSizeList = [str(serial.EIGHTBITS), str(serial.FIVEBITS),
62 | str(serial.SIXBITS),
63 | str(serial.SEVENBITS)]
64 | self.byteSizeComboBox.addItems(byteSizeList)
65 | self.configCoonLayout.addWidget(self.byteSizeComboBox)
66 |
67 | self.stopBitsLabel = QtWidgets.QLabel(self)
68 | self.stopBitsLabel.setObjectName('stopBitsLabel')
69 | self.configCoonLayout.addWidget(self.stopBitsLabel)
70 |
71 | self.stopBitsComboBox = QtWidgets.QComboBox(self)
72 | byteStopBitsList = [str(serial.STOPBITS_ONE),
73 | str(serial.STOPBITS_ONE_POINT_FIVE),
74 | str(serial.STOPBITS_TWO)]
75 | self.stopBitsComboBox.addItems(byteStopBitsList)
76 | self.stopBitsComboBox.setObjectName('stopBitsComboBox')
77 | self.configCoonLayout.addWidget(self.stopBitsComboBox)
78 |
79 | self.connectDisconnectButton = QtWidgets.QPushButton(self)
80 | self.connectDisconnectButton.setIcon(
81 | GUIToolKit.getIconByName('connect'))
82 | self.connectDisconnectButton.setObjectName('connectDeviceButton')
83 | self.connectDisconnectButton.setText('连接')
84 | self.connectDisconnectButton.clicked.connect(
85 | self.connectDisconnectDeviceAction)
86 |
87 | self.configCoonLayout.addWidget(self.connectDisconnectButton)
88 |
89 | self.portNameLabel.setText('端口号')
90 | self.bitRateLabel.setText('波特率')
91 | self.parityLabel.setText('奇偶校验')
92 | self.byteSizeLabel.setText('字节数')
93 | self.stopBitsLabel.setText('停止位')
94 |
95 | self.device.addConnectionStateListener(self)
96 | self.connectionStateChanged(self.device.isConnected)
97 |
98 | def getConfigValues(self):
99 | values = {
100 | 'connectionID': '',
101 | 'serialPortName': self.portNameComboBox.currentText(),
102 | 'serialRate': self.bitRatelineEdit.text(),
103 | 'stopBits': self.stopBitsExtractor(self.stopBitsComboBox.currentText()),
104 | 'serialByteSize': int(str(self.byteSizeComboBox.currentText())),
105 | 'serialParity': list(serial.PARITY_NAMES.keys())[list(serial.PARITY_NAMES.values()).index(self.parityComboBox.currentText())][0]
106 | }
107 | return values
108 |
109 | def stopBitsExtractor(self, value):
110 | if value == '1.5':
111 | return float(self.stopBitsComboBox.currentText())
112 | else:
113 | return int(self.stopBitsComboBox.currentText())
114 |
115 | def connectionStateChanged(self, isConnectedFlag):
116 | if isConnectedFlag:
117 | self.connectDisconnectButton.setText('断开')
118 | self.connectDisconnectButton.setIcon(
119 | GUIToolKit.getIconByName('disconnect'))
120 | else:
121 | self.connectDisconnectButton.setText('连接')
122 | self.connectDisconnectButton.setIcon(
123 | GUIToolKit.getIconByName('connect'))
124 |
125 | self.portNameLabel.setEnabled(not isConnectedFlag)
126 | self.portNameComboBox.setEnabled(not isConnectedFlag)
127 | self.bitRateLabel.setEnabled(not isConnectedFlag)
128 | self.bitRatelineEdit.setEnabled(not isConnectedFlag)
129 | self.parityLabel.setEnabled(not isConnectedFlag)
130 | self.parityComboBox.setEnabled(not isConnectedFlag)
131 | self.byteSizeLabel.setEnabled(not isConnectedFlag)
132 | self.byteSizeComboBox.setEnabled(not isConnectedFlag)
133 | self.stopBitsLabel.setEnabled(not isConnectedFlag)
134 | self.stopBitsComboBox.setEnabled(not isConnectedFlag)
135 |
136 | def connectDisconnectDeviceAction(self):
137 | if self.device.isConnected:
138 | self.disConnectAction()
139 | else:
140 | self.connectAction()
141 |
142 | def connectAction(self):
143 | deviceConfig = self.getConfigValues()
144 | self.device.configureConnection(deviceConfig)
145 | self.device.connect(SimpleFOCDevice.ONLY_CONNECT)
146 |
147 | def disConnectAction(self):
148 | self.device.disConnect()
--------------------------------------------------------------------------------
/src/gui/configtool/configureConnectionDialog.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import serial
4 | from PyQt5 import QtCore, QtGui, QtWidgets
5 |
6 | from src.gui.sharedcomnponets.sharedcomponets import SerialPortComboBox
7 | from src.simpleFOCConnector import SimpleFOCDevice
8 |
9 |
10 | class ConfigureSerailConnectionDialog(QtWidgets.QDialog):
11 | def __init__(self, parent=None):
12 | super().__init__(parent)
13 |
14 | self.setupUi(SimpleFOCDevice.getInstance())
15 |
16 | def setupUi(self, device=None):
17 | self.setObjectName('Dialog')
18 | self.resize(700, 188)
19 |
20 | self.gridLayout = QtWidgets.QGridLayout(self)
21 | self.gridLayout.setObjectName('gridLayout')
22 |
23 | self.portNameLabel = QtWidgets.QLabel(self)
24 | self.portNameLabel.setObjectName('portNameLabel')
25 | self.gridLayout.addWidget(self.portNameLabel, 0, 0, 1, 1)
26 |
27 | self.portNameComboBox = SerialPortComboBox(self)
28 | self.portNameComboBox.setObjectName('portNameComboBox')
29 | self.portNameComboBox.setMinimumWidth(250)
30 | self.gridLayout.addWidget(self.portNameComboBox, 0, 1, 1, 1)
31 |
32 | self.bitRateLabel = QtWidgets.QLabel(self)
33 | self.bitRateLabel.setObjectName('bitRateLabel')
34 | self.gridLayout.addWidget(self.bitRateLabel, 0, 2, 1, 1)
35 |
36 |
37 |
38 | self.bitRatelineEdit = QtWidgets.QLineEdit(self)
39 | self.bitRatelineEdit.setObjectName('bitRatelineEdit')
40 | self.bitRatelineEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp("^[0-9]*$")))
41 | self.gridLayout.addWidget(self.bitRatelineEdit, 0, 3, 1, 1)
42 |
43 | self.parityLabel = QtWidgets.QLabel(self)
44 | self.parityLabel.setObjectName('parityLabel')
45 | self.gridLayout.addWidget(self.parityLabel, 1, 0, 1, 1)
46 |
47 | self.parityComboBox = QtWidgets.QComboBox(self)
48 | self.parityComboBox.setObjectName('parityComboBox')
49 | self.parityComboBox.addItems(serial.PARITY_NAMES.values())
50 | self.gridLayout.addWidget(self.parityComboBox, 1, 1, 1, 1)
51 |
52 | serial.PARITY_NAMES.values()
53 |
54 | self.byteSizeLabel = QtWidgets.QLabel(self)
55 | self.byteSizeLabel.setObjectName('byteSizeLabel')
56 | self.gridLayout.addWidget(self.byteSizeLabel, 1, 2, 1, 1)
57 |
58 | self.byteSizeComboBox = QtWidgets.QComboBox(self)
59 | self.byteSizeComboBox.setObjectName('byteSizeComboBox')
60 | byteSizeList = [str(serial.EIGHTBITS), str(serial.FIVEBITS), str(serial.SIXBITS),
61 | str(serial.SEVENBITS)]
62 | self.byteSizeComboBox.addItems(byteSizeList)
63 | self.gridLayout.addWidget(self.byteSizeComboBox, 1, 3, 1, 1)
64 |
65 | self.stopBitsLabel = QtWidgets.QLabel(self)
66 | self.stopBitsLabel.setObjectName('stopBitsLabel')
67 | self.gridLayout.addWidget(self.stopBitsLabel, 2, 0, 1, 1)
68 |
69 | self.stopBitsComboBox = QtWidgets.QComboBox(self)
70 | byteStopBitsList = [str(serial.STOPBITS_ONE),
71 | str(serial.STOPBITS_ONE_POINT_FIVE),
72 | str(serial.STOPBITS_TWO)]
73 | self.stopBitsComboBox.addItems(byteStopBitsList)
74 | self.stopBitsComboBox.setObjectName('stopBitsComboBox')
75 | self.gridLayout.addWidget(self.stopBitsComboBox, 2, 1, 1, 1)
76 |
77 | self.connectionIDLabel = QtWidgets.QLabel(self)
78 | self.connectionIDLabel.setObjectName('connectionNameLabel')
79 | self.gridLayout.addWidget(self.connectionIDLabel, 2, 2, 1, 1)
80 |
81 | self.connectionIDlineEdit = QtWidgets.QLineEdit(self)
82 | self.connectionIDlineEdit.setMaxLength(10)
83 | self.connectionIDlineEdit.setObjectName('connectionNameEdit')
84 | self.gridLayout.addWidget(self.connectionIDlineEdit, 2, 3, 1, 1)
85 |
86 | self.buttonBox = QtWidgets.QDialogButtonBox(self)
87 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
88 | self.buttonBox.setStandardButtons(
89 | QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
90 | self.buttonBox.setObjectName('buttonBox')
91 |
92 | self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 4)
93 |
94 | self.setWindowTitle('Configure serial connection')
95 | self.portNameLabel.setText('端口号')
96 | self.bitRateLabel.setText('波特率')
97 | self.parityLabel.setText('奇偶校验')
98 | self.byteSizeLabel.setText('字节数')
99 | self.stopBitsLabel.setText('停止位')
100 | self.connectionIDLabel.setText('命令ID')
101 |
102 | self.buttonBox.accepted.connect(self.accept)
103 | self.buttonBox.rejected.connect(self.reject)
104 |
105 | QtCore.QMetaObject.connectSlotsByName(self)
106 |
107 | if device is not None:
108 | self.fillForm(device)
109 |
110 | def fillForm(self, deviceConnector):
111 | self.connectionIDlineEdit.setText(deviceConnector.connectionID)
112 | self.portNameComboBox.setCurrentText(deviceConnector.serialPortName)
113 | self.bitRatelineEdit.setText(str(deviceConnector.serialRate))
114 | self.stopBitsComboBox.setCurrentText(str(deviceConnector.stopBits))
115 | self.byteSizeComboBox.setCurrentText(str(deviceConnector.serialByteSize))
116 | self.parityComboBox.setCurrentText(str(deviceConnector.serialParity))
117 |
118 | def getConfigValues(self):
119 | values = {
120 | 'connectionID': self.connectionIDlineEdit.text(),
121 | 'serialPortName': self.portNameComboBox.currentText(),
122 | 'serialRate': self.bitRatelineEdit.text(),
123 | 'stopBits': self.stopBitsExtractor(self.stopBitsComboBox.currentText()),
124 | 'serialByteSize': int(str(self.byteSizeComboBox.currentText())),
125 | 'serialParity': list(serial.PARITY_NAMES.keys())[list(serial.PARITY_NAMES.values()).index(self.parityComboBox.currentText())][0]
126 | }
127 | return values
128 |
129 | def stopBitsExtractor(self, value):
130 | if value == '1.5':
131 | return float(self.stopBitsComboBox.currentText())
132 | else:
133 | return int(self.stopBitsComboBox.currentText())
--------------------------------------------------------------------------------
/src/gui/configtool/connectionControl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 |
5 | from src.gui.configtool.configureConnectionDialog import \
6 | ConfigureSerailConnectionDialog
7 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
8 | from src.simpleFOCConnector import SimpleFOCDevice
9 |
10 |
11 | class ConnectionControlGroupBox(QtWidgets.QGroupBox):
12 |
13 | def __init__(self, parent=None):
14 | super().__init__(parent)
15 |
16 | self.device = SimpleFOCDevice.getInstance()
17 |
18 | self.setObjectName('connectionControl')
19 | self.setTitle('连接')
20 |
21 | self.horizontalLayout = QtWidgets.QHBoxLayout(self)
22 | self.horizontalLayout.setObjectName('generalControlHL')
23 |
24 | self.devCommandIDLabel = QtWidgets.QLabel("命令ID:")
25 | self.horizontalLayout.addWidget(self.devCommandIDLabel)
26 |
27 | self.devCommandIDLetter = QtWidgets.QLineEdit()
28 | self.devCommandIDLetter.setObjectName('devCommandIDLetter')
29 | self.devCommandIDLetter.editingFinished.connect(self.changeDevicedevCommandID)
30 | self.horizontalLayout.addWidget(self.devCommandIDLetter)
31 | self.devCommandIDLetter.setText(self.device.devCommandID)
32 |
33 | self.pullConfig = QtWidgets.QPushButton()
34 | self.pullConfig.setObjectName('pullConfig')
35 | self.pullConfig.setIcon(GUIToolKit.getIconByName('pull'))
36 | self.pullConfig.setText('获取参数')
37 | self.pullConfig.clicked.connect(self.device.pullConfiguration)
38 |
39 | self.horizontalLayout.addWidget(self.pullConfig)
40 |
41 | self.connectDisconnectButton = QtWidgets.QPushButton(self)
42 | self.connectDisconnectButton.setIcon(GUIToolKit.getIconByName('connect'))
43 | self.connectDisconnectButton.setObjectName('connectDeviceButton')
44 | self.connectDisconnectButton.setText('连接')
45 | self.connectDisconnectButton.clicked.connect(self.connectDisconnectDeviceAction)
46 |
47 | self.horizontalLayout.addWidget(self.connectDisconnectButton)
48 |
49 | self.configureDeviceButton = QtWidgets.QPushButton(self)
50 | self.configureDeviceButton.setIcon(GUIToolKit.getIconByName('configure'))
51 | self.configureDeviceButton.setObjectName('configureDeviceButton')
52 | self.configureDeviceButton.setText('设置')
53 | self.configureDeviceButton.clicked.connect(self.configureDeviceAction)
54 | self.horizontalLayout.addWidget(self.configureDeviceButton)
55 |
56 | self.device.addConnectionStateListener(self)
57 | self.connectionStateChanged(self.device.isConnected)
58 |
59 | def changeDevicedevCommandID(self):
60 | self.device.devCommandID = self.devCommandIDLetter.text()
61 |
62 | def connectDisconnectDeviceAction(self):
63 | if self.device.isConnected:
64 | self.device.disConnect()
65 | else:
66 | connectionMode = SimpleFOCDevice.PULL_CONFIG_ON_CONNECT
67 | self.device.connect(connectionMode)
68 |
69 | def connectionStateChanged(self, isConnected):
70 | if isConnected:
71 | self.connectDisconnectButton.setIcon(
72 | GUIToolKit.getIconByName('disconnect'))
73 | self.connectDisconnectButton.setText('断开')
74 | else:
75 | self.connectDisconnectButton.setIcon(
76 | GUIToolKit.getIconByName('connect'))
77 | self.connectDisconnectButton.setText('连接')
78 |
79 | def configureDeviceAction(self):
80 | dialog = ConfigureSerailConnectionDialog()
81 | result = dialog.exec_()
82 | if result:
83 | deviceConfig = dialog.getConfigValues()
84 | self.device.configureConnection(deviceConfig)
85 |
--------------------------------------------------------------------------------
/src/gui/configtool/controlLoopConfig.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 |
5 | from src.simpleFOCConnector import SimpleFOCDevice
6 |
7 |
8 | class ControlLoopGroupBox(QtWidgets.QGroupBox):
9 | def __init__(self, parent=None):
10 | super().__init__(parent)
11 |
12 | self.device = SimpleFOCDevice.getInstance()
13 |
14 | self.setObjectName('controlLoop')
15 | self.setTitle('控制环模式')
16 |
17 | self.controlLoopHorizontalLayout = QtWidgets.QHBoxLayout(self)
18 | self.controlLoopHorizontalLayout.setObjectName('controlLoopHorizontalLayout')
19 |
20 |
21 | self.selectorControlLoop = QtWidgets.QComboBox(self)
22 | self.selectorControlLoop.setObjectName('selectorControlLoop')
23 | self.selectorControlLoop.addItems(['力矩', '速度', '角度', '速度开环', '角度开环'])
24 | self.selectorControlLoop.currentIndexChanged.connect(self.changeControlLoop)
25 | self.controlLoopHorizontalLayout.addWidget(self.selectorControlLoop)
26 |
27 | self.setControlLopMode(self.device.controlType)
28 |
29 | self.disableUI()
30 | self.device.addConnectionStateListener(self)
31 | self.device.commProvider.commandDataReceived.connect(
32 | self.commandResponseReceived)
33 |
34 | self.connectionStateChanged(self.device.isConnected)
35 |
36 | def connectionStateChanged(self, deviceConnected):
37 | if deviceConnected is True:
38 | self.enabeUI()
39 | else:
40 | self.disableUI()
41 |
42 | def enabeUI(self):
43 | self.setEnabled(True)
44 |
45 | def disableUI(self):
46 | self.setEnabled(False)
47 |
48 | def setControlLopMode(self, value):
49 | if value == SimpleFOCDevice.TORQUE_CONTROL:
50 | self.selectorControlLoop.setCurrentIndex(0)
51 | elif value == SimpleFOCDevice.VELOCITY_CONTROL:
52 | self.selectorControlLoop.setCurrentIndex(1)
53 | elif value == SimpleFOCDevice.ANGLE_CONTROL:
54 | self.selectorControlLoop.setCurrentIndex(2)
55 | elif value == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL:
56 | self.selectorControlLoop.setCurrentIndex(3)
57 | elif value == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL:
58 | self.selectorControlLoop.setCurrentIndex(4)
59 |
60 | def changeControlLoop(self):
61 | index = self.selectorControlLoop.currentIndex()
62 | if index == 0:
63 | self.device.sendControlType(SimpleFOCDevice.TORQUE_CONTROL)
64 | elif index == 1:
65 | self.device.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL)
66 | elif index == 2:
67 | self.device.sendControlType(SimpleFOCDevice.ANGLE_CONTROL)
68 | elif index == 3:
69 | self.device.sendControlType(SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL)
70 | elif index == 4:
71 | self.device.sendControlType(SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL)
72 |
73 | def commandResponseReceived(self, cmdRespose):
74 | self.setControlLopMode(self.device.controlType)
--------------------------------------------------------------------------------
/src/gui/configtool/deviceConfigurationTool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 |
5 | from src.gui.configtool.connectionControl import ConnectionControlGroupBox
6 | from src.gui.configtool.controlLoopConfig import ControlLoopGroupBox
7 | from src.gui.configtool.deviceJoggingControl import DeviceJoggingControl
8 | from src.gui.configtool.droDisplayWidget import DROGroupBox
9 | from src.gui.configtool.generalControls import GeneralControls
10 | from src.gui.configtool.generalSettingsWidget import GeneralSettingsGroupBox
11 | from src.gui.configtool.graphicWidget import SimpleFOCGraphicWidget
12 | from src.gui.configtool.pidConfiguration import PidGroupBox
13 | from src.gui.configtool.torqueConfig import TorqueGroupBox
14 | from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget
15 | from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget,
16 | GUIToolKit)
17 | from src.simpleFOCConnector import SimpleFOCDevice
18 |
19 |
20 | class DeviceConfigurationTool(WorkAreaTabWidget):
21 |
22 | def __init__(self, parent=None):
23 | super().__init__(parent)
24 |
25 | self.device = SimpleFOCDevice.getInstance()
26 |
27 | self.setObjectName('DeviceConfigurationTool')
28 |
29 | self.verticalLayout = QtWidgets.QVBoxLayout(self)
30 | self.verticalLayout.setObjectName('verticalLayout')
31 |
32 | self.counterWidget = QtWidgets.QWidget(self)
33 | self.counterWidget.setObjectName('counterWidget')
34 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.counterWidget)
35 | self.horizontalLayout.setObjectName('horizontalLayout')
36 | self.digitalReadOut = DROGroupBox(self.counterWidget)
37 | self.horizontalLayout.addWidget(self.digitalReadOut)
38 |
39 | self.controlLoop = ControlLoopGroupBox(self.counterWidget)
40 | self.horizontalLayout.addWidget(self.controlLoop)
41 |
42 | self.torqueConfig = TorqueGroupBox(self.counterWidget)
43 | self.horizontalLayout.addWidget(self.torqueConfig)
44 |
45 | self.connectionControl = ConnectionControlGroupBox(self.counterWidget)
46 | self.horizontalLayout.addWidget(self.connectionControl)
47 | self.verticalLayout.addWidget(self.counterWidget)
48 |
49 | self.graphicWidget = SimpleFOCGraphicWidget()
50 | self.verticalLayout.addWidget(self.graphicWidget)
51 |
52 | self.bottomWidget = QtWidgets.QWidget(self)
53 | self.bottomWidget.setObjectName('bottomWidget')
54 |
55 | self.bottomHorizontalLayout = QtWidgets.QHBoxLayout(self.bottomWidget)
56 | self.bottomHorizontalLayout.setObjectName('configureHorizontalLayout')
57 |
58 | self.pidConfigurator = PidGroupBox(self.bottomWidget)
59 | self.bottomHorizontalLayout.addWidget(self.pidConfigurator)
60 |
61 | self.generalLayout = QtWidgets.QVBoxLayout()
62 | self.generalDeviceSettings = GeneralSettingsGroupBox(self.bottomWidget)
63 |
64 | self.generalControls = GeneralControls(self.bottomWidget)
65 | self.generalLayout.addWidget(self.generalControls)
66 | self.generalLayout.addWidget(self.generalDeviceSettings)
67 | self.bottomHorizontalLayout.addLayout(self.generalLayout)
68 |
69 | self.lasWidget = QtWidgets.QWidget(self)
70 | self.lastVerticalLayout = QtWidgets.QVBoxLayout(self.lasWidget)
71 |
72 | self.commandLine = CommandLineWidget(self)
73 | self.lastVerticalLayout.addWidget(self.commandLine)
74 |
75 | self.joggingControl = DeviceJoggingControl(self)
76 | self.lastVerticalLayout.addWidget(self.joggingControl)
77 |
78 | self.bottomHorizontalLayout.addWidget(self.lasWidget)
79 | self.verticalLayout.addWidget(self.bottomWidget)
80 |
81 | self.device.commProvider.commandDataReceived.connect(self.commandLine.publishCommandResponseData)
82 |
83 | def getTabIcon(self):
84 | return GUIToolKit.getIconByName('motor')
85 |
86 | def getTabName(self):
87 | return self.device.connectionID
88 |
89 | def configureConnection(self, configvalues):
90 | self.device.serialPortName = configvalues['serialPortName']
91 | self.device.serialRate = configvalues['serialRate']
92 | self.device.stopBits = configvalues['stopBits']
93 | self.device.serialByteSize = configvalues['serialByteSize']
94 | self.device.serialParity = configvalues['serialParity']
95 |
--------------------------------------------------------------------------------
/src/gui/configtool/deviceInteractionFrame.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5.QtCore import Qt
4 | from PyQt5.QtWidgets import (QVBoxLayout, QFrame, QSplitter)
5 |
6 | from src.gui.configtool.graphicWidget import SimpleFOCGraphicWidget
7 | from src.gui.sharedcomnponets.commandLineInterface import CommandLineWidget
8 | from src.simpleFOCConnector import SimpleFOCDevice
9 |
10 |
11 | class DeviceInteractionFrame(QFrame):
12 | def __init__(self, parent=None):
13 | super().__init__(parent)
14 |
15 | self.layout = QVBoxLayout(self)
16 | self.setLayout(self.layout)
17 | self.device = SimpleFOCDevice.getInstance()
18 |
19 | self.graphicWidget = SimpleFOCGraphicWidget(self)
20 | self.cmdLineTollWidget = CommandLineWidget(self)
21 |
22 | self.cmdLineTollWidget.setMaximumHeight(150)
23 |
24 | self.verticalSplitter = QSplitter(Qt.Vertical)
25 | self.verticalSplitter.addWidget(self.graphicWidget)
26 | self.verticalSplitter.addWidget(self.cmdLineTollWidget)
27 | self.device.commProvider.commandDataReceived.connect(
28 | self.cmdLineTollWidget.publishCommandResponseData)
29 | self.layout.addWidget(self.verticalSplitter)
30 |
31 | self.setLayout(self.layout)
32 |
--------------------------------------------------------------------------------
/src/gui/configtool/deviceJoggingControl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | from PyQt5 import QtGui, QtWidgets, QtCore
5 |
6 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
7 | from src.simpleFOCConnector import SimpleFOCDevice
8 |
9 |
10 | class DeviceJoggingControl(QtWidgets.QGroupBox):
11 | def __init__(self, parent=None):
12 | super().__init__(parent)
13 | self.device = SimpleFOCDevice.getInstance()
14 |
15 | self.setObjectName('joggingControl')
16 | self.setTitle('点动控制')
17 |
18 | self.horizontalLayout = QtWidgets.QHBoxLayout(self)
19 | self.horizontalLayout.setObjectName('generalControlHL')
20 |
21 | self.fastFordwardButton = QtWidgets.QPushButton()
22 | self.fastFordwardButton.setObjectName('fastbackward')
23 | self.fastFordwardButton.setIcon(GUIToolKit.getIconByName('fastbackward'))
24 | self.fastFordwardButton.clicked.connect(self.joggingFastBackward)
25 | self.horizontalLayout.addWidget(self.fastFordwardButton)
26 |
27 | self.backwardButton = QtWidgets.QPushButton()
28 | self.backwardButton.setObjectName('backward')
29 | self.backwardButton.setIcon(GUIToolKit.getIconByName('backward'))
30 | self.backwardButton.clicked.connect(self.joggingBackward)
31 | self.horizontalLayout.addWidget(self.backwardButton)
32 |
33 | self.stopButton = QtWidgets.QPushButton()
34 | self.stopButton.setObjectName('stopbutton')
35 | self.stopButton.setIcon(GUIToolKit.getIconByName('stopjogging'))
36 | self.stopButton.clicked.connect(self.joggingStop)
37 | self.horizontalLayout.addWidget(self.stopButton)
38 |
39 | self.fordwardButton = QtWidgets.QPushButton()
40 | self.fordwardButton.setObjectName('fordward')
41 | self.fordwardButton.setIcon(GUIToolKit.getIconByName('fordward'))
42 | self.fordwardButton.clicked.connect(self.joggingFordward)
43 | self.horizontalLayout.addWidget(self.fordwardButton)
44 |
45 | self.fastBackwardButton = QtWidgets.QPushButton()
46 | self.fastBackwardButton.setObjectName('fastfordward')
47 | self.fastBackwardButton.setIcon(GUIToolKit.getIconByName('fastfordward'))
48 | self.fastBackwardButton.clicked.connect(self.joggingfastFordward)
49 | self.horizontalLayout.addWidget(self.fastBackwardButton)
50 |
51 | self.incrementLabel = QtWidgets.QLabel("Increment:")
52 | self.horizontalLayout.addWidget(self.incrementLabel)
53 |
54 | onlyFloat = QtGui.QRegExpValidator(
55 | QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+"))
56 | self.incrementEdit = QtWidgets.QLineEdit()
57 | self.incrementEdit.setValidator(onlyFloat)
58 | self.incrementEdit.setAlignment(QtCore.Qt.AlignCenter)
59 | self.incrementEdit.setText('1.0')
60 | self.incrementEdit.setObjectName('incrementEdit')
61 | self.horizontalLayout.addWidget(self.incrementEdit)
62 |
63 | self.disableUI()
64 | self.device.addConnectionStateListener(self)
65 |
66 | def connectionStateChanged(self, isConnectedFlag):
67 | if isConnectedFlag is True:
68 | self.enabeUI()
69 | else:
70 | self.disableUI()
71 | def enabeUI(self):
72 | self.setEnabled(True)
73 |
74 | def disableUI(self):
75 | self.setEnabled(False)
76 |
77 | def joggingFastBackward(self):
78 | currenttarget = self.device.targetNow
79 | increment = self.incrementEdit.text()
80 | newTarget = float(currenttarget) - 2 * float(increment)
81 | self.device.sendTargetValue(str(newTarget))
82 | def joggingBackward(self):
83 | increment = self.incrementEdit.text()
84 | currenttarget = self.device.targetNow
85 | newTarget = float(currenttarget) - float(increment)
86 | self.device.sendTargetValue(str(newTarget))
87 | def joggingStop(self):
88 | controltType = self.device.controlType
89 | if (controltType == SimpleFOCDevice.ANGLE_CONTROL or
90 | controltType == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL):
91 | self.device.sendTargetValue(self.device.angleNow)
92 | if (controltType == SimpleFOCDevice.VELOCITY_CONTROL or
93 | controltType == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL):
94 | self.device.sendTargetValue('0')
95 | def joggingFordward(self):
96 | increment = self.incrementEdit.text()
97 | currenttarget = self.device.targetNow
98 | newTarget = float(currenttarget) + float(increment)
99 | self.device.sendTargetValue(str(newTarget))
100 | def joggingfastFordward(self):
101 | increment = self.incrementEdit.text()
102 | currenttarget = self.device.targetNow
103 | newTarget = float(currenttarget) + 2 * float(increment)
104 | self.device.sendTargetValue(str(newTarget))
--------------------------------------------------------------------------------
/src/gui/configtool/deviceTreeview.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets, QtCore
4 | from PyQt5.Qt import QTreeWidget
5 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
6 | from src.simpleFOCConnector import SimpleFOCDevice
7 | from src.simpleFOCConnector import Command
8 |
9 | class DeviceTreeView(QTreeWidget):
10 | def __init__(self, parent=None):
11 | super().__init__(parent)
12 |
13 | self.device = SimpleFOCDevice.getInstance()
14 |
15 | self.sFOCDevice = QtWidgets.QTreeWidgetItem(self)
16 | self.sFOCDevice.setText(0, 'sFOC 设备')
17 | self.sFOCDevice.setIcon(0, GUIToolKit.getIconByName('motor'))
18 |
19 | self.setColumnCount(2)
20 |
21 | self.motionControl = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
22 | self.motionControl.setText(0, '运动控制设置')
23 | self.motionControl.setIcon(0, GUIToolKit.getIconByName('pidconfig'))
24 | self.sFOCDevice.addChild(self.motionControl)
25 |
26 | self.controller = QtWidgets.QTreeWidgetItem(self.motionControl)
27 | self.controller.setText(0, '运动控制类型')
28 | self.controller.setIcon(0, GUIToolKit.getIconByName('gear'))
29 | self.selectorControlLoop = QtWidgets.QComboBox(self)
30 | self.selectorControlLoop.addItems(['力矩控制', '速度控制', '角度控制', '速度开环控制', '角度开环控制'])
31 | self.selectorControlLoop.currentIndexChanged.connect(self.changeControlLoop)
32 | self.setItemWidget(self.controller,1,self.selectorControlLoop)
33 |
34 | self.torque = QtWidgets.QTreeWidgetItem(self.motionControl)
35 | self.torque.setText(0, '力矩控制类型')
36 | self.torque.setIcon(0, GUIToolKit.getIconByName('gear'))
37 | self.selectorTorque = QtWidgets.QComboBox(self)
38 | self.selectorTorque.addItems(['电压', 'DC电流', 'FOC电流'])
39 | self.selectorTorque.currentIndexChanged.connect(self.changeTorque)
40 | self.setItemWidget(self.torque,1,self.selectorTorque)
41 |
42 | self.motionDownsample = QtWidgets.QTreeWidgetItem(self.motionControl)
43 | self.motionDownsample.setText(0, '运动控制频率降采样')
44 | self.motionDownsample.setIcon(0, GUIToolKit.getIconByName('gear'))
45 | self.motionDownsample.setText(1, '')
46 | self.motionDownsample.setFlags(
47 | self.motionDownsample.flags() | QtCore.Qt.ItemIsEditable)
48 |
49 | self.PIDVelocityConfig = self.addPIDSubtree(self.motionControl,'速度 PID')
50 | self.PIDAngleConfig = self.addPIDSubtree(self.motionControl,'角度 PID')
51 | self.PIDCurrentQConfig = self.addPIDSubtree(self.motionControl,'电流q PID')
52 | self.PIDCurrentDConfig = self.addPIDSubtree(self.motionControl,'电流d PID')
53 |
54 | self.limitsConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
55 | self.limitsConfig.setText(0, '限幅')
56 | self.limitsConfig.setIcon(0, GUIToolKit.getIconByName('statistics'))
57 | self.sFOCDevice.addChild(self.limitsConfig)
58 |
59 | self.velocityLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig)
60 | self.velocityLimit.setText(0, '速度限幅')
61 | self.velocityLimit.setIcon(0, GUIToolKit.getIconByName('gear'))
62 | self.velocityLimit.setText(1, '')
63 | self.velocityLimit.setFlags(
64 | self.velocityLimit.flags() | QtCore.Qt.ItemIsEditable)
65 |
66 | self.voltageLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig)
67 | self.voltageLimit.setText(0, '电压限幅')
68 | self.voltageLimit.setIcon(0, GUIToolKit.getIconByName('gear'))
69 | self.voltageLimit.setText(1, '')
70 | self.voltageLimit.setFlags(
71 | self.voltageLimit.flags() | QtCore.Qt.ItemIsEditable)
72 |
73 | self.currentLimit = QtWidgets.QTreeWidgetItem(self.limitsConfig)
74 | self.currentLimit.setText(0, '电流限幅')
75 | self.currentLimit.setIcon(0, GUIToolKit.getIconByName('gear'))
76 | self.currentLimit.setText(1, '')
77 | self.currentLimit.setFlags(
78 | self.currentLimit.flags() | QtCore.Qt.ItemIsEditable)
79 |
80 | self.statesConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
81 | self.statesConfig.setText(0, '状态')
82 | self.statesConfig.setIcon(0, GUIToolKit.getIconByName('statistics'))
83 | self.sFOCDevice.addChild(self.statesConfig)
84 |
85 | self.satateTarget = QtWidgets.QTreeWidgetItem(self.statesConfig)
86 | self.satateTarget.setText(0, '目标')
87 | self.satateTarget.setIcon(0, GUIToolKit.getIconByName('gear'))
88 | self.satateTarget.setText(1, '')
89 |
90 | self.stateVq = QtWidgets.QTreeWidgetItem(self.statesConfig)
91 | self.stateVq.setText(0, '电压 q')
92 | self.stateVq.setIcon(0, GUIToolKit.getIconByName('gear'))
93 | self.stateVd = QtWidgets.QTreeWidgetItem(self.statesConfig)
94 | self.stateVd.setText(0, '电压 d')
95 | self.stateVd.setIcon(0, GUIToolKit.getIconByName('gear'))
96 |
97 | self.stateCq = QtWidgets.QTreeWidgetItem(self.statesConfig)
98 | self.stateCq.setText(0, '电流 q')
99 | self.stateCq.setIcon(0, GUIToolKit.getIconByName('gear'))
100 | self.stateCd = QtWidgets.QTreeWidgetItem(self.statesConfig)
101 | self.stateCd.setText(0, '电流 d')
102 | self.stateCd.setIcon(0, GUIToolKit.getIconByName('gear'))
103 |
104 | self.stateVel = QtWidgets.QTreeWidgetItem(self.statesConfig)
105 | self.stateVel.setText(0, '速度')
106 | self.stateVel.setIcon(0, GUIToolKit.getIconByName('gear'))
107 | self.stateVel.setText(1, '')
108 |
109 | self.stateAngle = QtWidgets.QTreeWidgetItem(self.statesConfig)
110 | self.stateAngle.setText(0, '角度')
111 | self.stateAngle.setIcon(0, GUIToolKit.getIconByName('gear'))
112 | self.stateAngle.setText(1, '')
113 |
114 | self.sensorConfig = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
115 | self.sensorConfig.setText(0, '位置传感器设置')
116 | self.sensorConfig.setIcon(0, GUIToolKit.getIconByName('sensor'))
117 | self.sFOCDevice.addChild(self.sensorConfig)
118 |
119 | self.sensorZeroOffset = QtWidgets.QTreeWidgetItem(self.sensorConfig)
120 | self.sensorZeroOffset.setText(0, '零点偏置')
121 | self.sensorZeroOffset.setIcon(0, GUIToolKit.getIconByName('gear'))
122 | self.sensorZeroOffset.setText(1, '')
123 | self.sensorZeroOffset.setFlags(
124 | self.sensorZeroOffset.flags() | QtCore.Qt.ItemIsEditable)
125 |
126 | self.sensorZeroElecOffset = QtWidgets.QTreeWidgetItem(self.sensorConfig)
127 | self.sensorZeroElecOffset.setText(0, '电气零点偏置')
128 | self.sensorZeroElecOffset.setIcon(0, GUIToolKit.getIconByName('gear'))
129 | self.sensorZeroElecOffset.setText(1, '')
130 | self.sensorZeroElecOffset.setFlags(
131 | self.sensorZeroElecOffset.flags() | QtCore.Qt.ItemIsEditable)
132 |
133 | self.generalSettings = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
134 | self.generalSettings.setText(0, '通用设置')
135 | self.generalSettings.setIcon(0, GUIToolKit.getIconByName('generalsettings'))
136 | self.sFOCDevice.addChild(self.generalSettings)
137 |
138 | self.phaseRes = QtWidgets.QTreeWidgetItem(self.generalSettings)
139 | self.phaseRes.setText(0, '相电阻')
140 | self.phaseRes.setIcon(0, GUIToolKit.getIconByName('res'))
141 | self.phaseRes.setText(1, '')
142 | self.phaseRes.setFlags(
143 | self.phaseRes.flags() | QtCore.Qt.ItemIsEditable)
144 |
145 | self.deviceStatus = QtWidgets.QTreeWidgetItem(self.generalSettings)
146 | self.deviceStatus.setText(0, '电机状态')
147 | self.deviceStatus.setIcon(0, GUIToolKit.getIconByName('gear'))
148 | self.selectStatus = QtWidgets.QComboBox(self)
149 | self.selectStatus.addItems(['失能', '使能'])
150 | self.selectStatus.currentIndexChanged.connect(self.changeStatus)
151 | self.setItemWidget(self.deviceStatus,1,self.selectStatus)
152 |
153 | self.modulationType = QtWidgets.QTreeWidgetItem(self.generalSettings)
154 | self.modulationType.setText(0, 'PWM调制')
155 | self.modulationType.setIcon(0, GUIToolKit.getIconByName('gear'))
156 | self.selectModulation = QtWidgets.QComboBox(self)
157 | self.selectModulation.addItems(['正弦PWM调制', '空间矢量PWM调制', '梯形调制 120', '梯形调制 150'])
158 | self.selectModulation.currentIndexChanged.connect(self.changeModType)
159 | self.setItemWidget(self.modulationType,1,self.selectModulation)
160 |
161 | self.modulationCenter = QtWidgets.QTreeWidgetItem(self.generalSettings)
162 | self.modulationCenter.setText(0, 'Modulation center')
163 | self.modulationCenter.setIcon(0, GUIToolKit.getIconByName('gear'))
164 | self.selectModCenter = QtWidgets.QComboBox(self)
165 | self.selectModCenter.addItems(['Disabled', 'Enabled'])
166 | self.selectModCenter.currentIndexChanged.connect(self.changeModCenter)
167 | self.setItemWidget(self.modulationCenter,1,self.selectModCenter)
168 |
169 | self.customComands = QtWidgets.QTreeWidgetItem(self.sFOCDevice)
170 | self.customComands.setText(0, 'Custom commands')
171 | self.customComands.setIcon(0, GUIToolKit.getIconByName('customcommands'))
172 | self.sFOCDevice.addChild(self.customComands)
173 |
174 | for customCommand in self.device.customCommands.customCommandsList:
175 | self.initCustomCommand(customCommand)
176 |
177 | self.installEventFilter(self)
178 | self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
179 | self.customContextMenuRequested.connect(self.customCommandsMenu)
180 |
181 | self.header().resizeSection(0,230)
182 |
183 | self.setAlternatingRowColors(True)
184 | self.header().hide()
185 | self.expandItem(self.sFOCDevice)
186 | self.expandItem(self.motionControl)
187 |
188 | self.device.addConnectionStateListener(self)
189 | self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived)
190 | self.device.commProvider.stateMonitorReceived.connect(self.stateResponseReceived)
191 |
192 | self.itemChanged.connect(self.treeItemEdited)
193 |
194 | self.setEnabled(self.device.isConnected)
195 |
196 | def customCommandsMenu(self, position):
197 | indexes = self.selectedIndexes()
198 | if len(indexes) > 0:
199 | level = 0
200 | index = indexes[0]
201 | while index.parent().isValid():
202 | index = index.parent()
203 | level += 1
204 | selectedItem = self.selectedItems()[0]
205 | menu = QtWidgets.QMenu()
206 | if selectedItem.text(0) == 'Custom commands':
207 | addComand = QtWidgets.QAction("Add command", self)
208 | addComand.triggered.connect(self.addCommandAction)
209 | menu.addAction(addComand)
210 | elif hasattr(selectedItem, 'isCustomCommand'):
211 | executeCommand = QtWidgets.QAction("Execute", self)
212 | executeCommand.triggered.connect(self.executeCustomCommandAction)
213 | menu.addAction(executeCommand)
214 | deleteCommand = QtWidgets.QAction("Remove", self)
215 | deleteCommand.triggered.connect(self.deleteCustomCommand)
216 | menu.addAction(deleteCommand)
217 |
218 | menu.exec_(self.viewport().mapToGlobal(position))
219 |
220 | def addCommandAction(self):
221 | selectedItem = self.selectedItems()[0]
222 | self.addCustomCommand(selectedItem)
223 |
224 | def executeCustomCommandAction(self):
225 | selectedItem = self.selectedItems()[0]
226 | selectedCustomCommand = selectedItem.text(1)
227 | self.device.sendCommand(selectedCustomCommand)
228 |
229 | def deleteCustomCommand(self):
230 | root = self.invisibleRootItem()
231 | deletedIndex = self.customComands.indexOfChild(self.selectedItems()[0])
232 | self.device.customCommands.customCommandsList.pop(deletedIndex)
233 | for item in self.selectedItems():
234 | (item.parent() or root).removeChild(item)
235 |
236 | def eventFilter(self, obj, event):
237 | if event.type() == QtCore.QEvent.KeyPress:
238 | if event.key() == QtCore.Qt.Key_Return:
239 | selectedItem = self.selectedItems()[0]
240 | if selectedItem.text(0) == 'Custom commands':
241 | self.addCustomCommand(selectedItem)
242 | if event.key() == QtCore.Qt.Key_Space or event.key() == QtCore.Qt.Key_Right:
243 | selectedItem = self.selectedItems()[0]
244 | if selectedItem.parent().text(0) == 'Custom commands':
245 | self.executeCustomCommandAction()
246 | if event.key() == QtCore.Qt.Key_Delete or event.key() == QtCore.Qt.Key_Backspace:
247 | selectedItem = self.selectedItems()[0]
248 | if selectedItem.parent().text(0) == 'Custom commands':
249 | self.deleteCustomCommand()
250 | return super(DeviceTreeView, self).eventFilter(obj, event)
251 |
252 | def addCustomCommand(sefl,selectedItem):
253 | customCommand = QtWidgets.QTreeWidgetItem()
254 | customCommand.isCustomCommand = True
255 | customCommand.setText(0, '命令')
256 | customCommand.setIcon(0, GUIToolKit.getIconByName('gear'))
257 |
258 | customCommand.setFlags(
259 | customCommand.flags() | QtCore.Qt.ItemIsEditable)
260 | selectedItem.addChild(customCommand)
261 | sefl.device.customCommands.customCommandsList.append(Command('Command',''))
262 |
263 | def initCustomCommand(sefl, command):
264 | customCommand = QtWidgets.QTreeWidgetItem()
265 | customCommand.isCustomCommand = True
266 | customCommand.setText(0, command.cmdName)
267 | customCommand.setText(1, command.cmd)
268 | customCommand.setIcon(0, GUIToolKit.getIconByName('gear'))
269 | customCommand.setFlags(
270 | customCommand.flags() | QtCore.Qt.ItemIsEditable)
271 | sefl.customComands.addChild(customCommand)
272 |
273 | def addPIDSubtree(self, parent, label):
274 | pidConfiguration = QtWidgets.QTreeWidgetItem()
275 | pidConfiguration.setText(0, label)
276 | pidConfiguration.setIcon(0, GUIToolKit.getIconByName('pidconfig'))
277 | parent.addChild(pidConfiguration)
278 |
279 | proportionalGain = QtWidgets.QTreeWidgetItem(pidConfiguration)
280 | proportionalGain.setText(0, 'P 比例项')
281 | proportionalGain.setIcon(0, GUIToolKit.getIconByName('gear'))
282 | proportionalGain.setText(1, '')
283 | proportionalGain.setFlags(
284 | proportionalGain.flags() | QtCore.Qt.ItemIsEditable)
285 |
286 | integralGain = QtWidgets.QTreeWidgetItem(pidConfiguration)
287 | integralGain.setText(0, 'I 积分项')
288 | integralGain.setIcon(0, GUIToolKit.getIconByName('gear'))
289 | integralGain.setText(1, '')
290 | integralGain.setFlags(
291 | integralGain.flags() | QtCore.Qt.ItemIsEditable)
292 |
293 | derivativeGain = QtWidgets.QTreeWidgetItem(pidConfiguration)
294 | derivativeGain.setText(0, 'D 微分项')
295 | derivativeGain.setIcon(0, GUIToolKit.getIconByName('gear'))
296 | derivativeGain.setText(1, '')
297 | derivativeGain.setFlags(
298 | derivativeGain.flags() | QtCore.Qt.ItemIsEditable)
299 |
300 | voltageRamp = QtWidgets.QTreeWidgetItem(pidConfiguration)
301 | voltageRamp.setText(0, '斜坡输出')
302 | voltageRamp.setIcon(0, GUIToolKit.getIconByName('gear'))
303 | voltageRamp.setText(1, '')
304 | voltageRamp.setFlags(
305 | voltageRamp.flags() | QtCore.Qt.ItemIsEditable)
306 |
307 | limit = QtWidgets.QTreeWidgetItem(pidConfiguration)
308 | limit.setText(0, '输出限幅')
309 | limit.setIcon(0, GUIToolKit.getIconByName('gear'))
310 | limit.setText(1, '')
311 | limit.setFlags(
312 | limit.flags() | QtCore.Qt.ItemIsEditable)
313 |
314 | lpfTf = QtWidgets.QTreeWidgetItem(pidConfiguration)
315 | lpfTf.setText(0, '低通滤波器')
316 | lpfTf.setIcon(0, GUIToolKit.getIconByName('gear'))
317 | lpfTf.setText(1, '')
318 | lpfTf.setFlags(
319 | lpfTf.flags() | QtCore.Qt.ItemIsEditable)
320 |
321 | return pidConfiguration
322 |
323 | def treeItemEdited(self, item, column):
324 | if item.parent().text(0) == 'Custom commands':
325 | updatedIndex = self.customComands.indexOfChild(item)
326 | updatedValue = item.text(column)
327 | if column == 0:
328 | self.device.customCommands.customCommandsList[
329 | updatedIndex].cmdName = updatedValue
330 | else:
331 | self.device.customCommands.customCommandsList[
332 | updatedIndex].cmd = updatedValue
333 | else:
334 | self.sendCommand(item, column)
335 |
336 | def sendCommand(self, item, column):
337 | value = item.text(1)
338 | fieldName = item.text(0)
339 | pidLabel = item.parent().text(0)
340 | pid = {}
341 | lpf = {}
342 | if '速度 PID' in pidLabel:
343 | pid = self.device.PIDVelocity
344 | lpf = self.device.LPFVelocity
345 | elif '角度 PID' in pidLabel:
346 | pid = self.device.PIDAngle
347 | lpf = self.device.LPFAngle
348 | elif '电流Q PID' in pidLabel:
349 | pid = self.device.PIDCurrentQ
350 | lpf = self.device.LPFCurrentQ
351 | elif '电流D PID' in pidLabel:
352 | pid = self.device.PIDCurrentD
353 | lpf = self.device.LPFCurrentD
354 |
355 | if 'P 比例项' in fieldName:
356 | self.device.sendProportionalGain(pid, value)
357 | elif 'I 积分项' in fieldName:
358 | self.device.sendIntegralGain(pid, value)
359 | elif 'D 微分项' in fieldName:
360 | self.device.sendDerivativeGain(pid, value)
361 | elif '斜坡输出' in fieldName:
362 | self.device.sendOutputRamp(pid, value)
363 | elif '低通滤波器' in fieldName:
364 | self.device.sendLowPassFilter(lpf, value)
365 | elif '输出限幅' in fieldName:
366 | self.device.sendOutputLimit(pid,value)
367 | elif '电压限幅' in fieldName:
368 | self.device.sendVoltageLimit(value)
369 | elif '速度限幅' in fieldName:
370 | self.device.sendVelocityLimit(value)
371 | elif '电流限幅' in fieldName:
372 | self.device.sendCurrentLimit(value)
373 | elif '相电阻' in fieldName:
374 | self.device.sendPhaseResistance(value)
375 | elif '零点偏置' in fieldName:
376 | self.device.sendSensorZeroOffset(value)
377 | elif '电气零点偏置' in fieldName:
378 | self.device.sendSensorZeroElectrical(value)
379 | elif '运动控制频率降采样' in fieldName:
380 | self.device.sendMotionDownsample(value)
381 |
382 | def refreshPIDSubtree(self, pidDisp, pidVal, lpfVal):
383 | pidDisp.child(0).setText(1,str(pidVal.P))
384 | pidDisp.child(1).setText(1,str(pidVal.I))
385 | pidDisp.child(2).setText(1,str(pidVal.D))
386 | pidDisp.child(3).setText(1,str(pidVal.outputRamp))
387 | pidDisp.child(4).setText(1,str(pidVal.outputLimit))
388 | pidDisp.child(5).setText(1,str(lpfVal.Tf))
389 |
390 | def commandResponseReceived(self, comandResponse):
391 | self.refreshDeviceTree()
392 | self.setTorqueMode(self.device.torqueType)
393 | self.setControlLopMode(self.device.controlType)
394 | self.setEnabledDisabled(self.device.deviceStatus)
395 | self.setModCenter(self.device.modulationCentered)
396 | self.setModType(self.device.modulationType)
397 |
398 | def stateResponseReceived(self, comandResponse):
399 | self.blockSignals(True)
400 | self.stateVel.setText(1,str(self.device.velocityNow))
401 | self.stateAngle.setText(1,str(self.device.angleNow))
402 | self.stateVd.setText(1,str(self.device.voltageDNow))
403 | self.stateVq.setText(1,str(self.device.voltageQNow))
404 | self.stateCq.setText(1,str(self.device.currentQNow))
405 | self.stateCd.setText(1,str(self.device.currentDNow))
406 | self.satateTarget.setText(1,str(self.device.targetNow))
407 | self.update()
408 | self.blockSignals(False)
409 |
410 | def refreshDeviceTree(self):
411 | self.blockSignals(True)
412 |
413 | self.refreshPIDSubtree( self.PIDVelocityConfig, self.device.PIDVelocity, self.device.LPFVelocity)
414 | self.refreshPIDSubtree( self.PIDAngleConfig, self.device.PIDAngle, self.device.LPFAngle)
415 | self.refreshPIDSubtree( self.PIDCurrentQConfig, self.device.PIDCurrentQ, self.device.LPFCurrentQ)
416 | self.refreshPIDSubtree( self.PIDCurrentDConfig, self.device.PIDCurrentD, self.device.LPFCurrentD)
417 |
418 | self.voltageLimit.setText(1,str(self.device.voltageLimit))
419 | self.velocityLimit.setText(1,str(self.device.velocityLimit))
420 | self.currentLimit.setText(1,str(self.device.currentLimit))
421 |
422 | self.sensorZeroOffset.setText(1,str(self.device.sensorZeroOffset))
423 | self.sensorZeroElecOffset.setText(1,str(self.device.sensorElectricalZero))
424 |
425 | self.phaseRes.setText(1,str(self.device.phaseResistance))
426 | # self.deviceStatus.setText(1,str(self.device.deviceStatus))
427 |
428 | self.motionDownsample.setText(1,str(self.device.motionDownsample))
429 | # self.torque.setText(1,str(self.device.torqueType))
430 | # self.controller.setText(1,str(self.device.controlType))
431 | self.update()
432 | self.blockSignals(False)
433 |
434 | def setTorqueMode(self, value):
435 | self.blockSignals(True)
436 | if value == SimpleFOCDevice.VOLTAGE_TORQUE:
437 | self.selectorTorque.setCurrentIndex(0)
438 | elif value == SimpleFOCDevice.DC_CURRENT_TORQUE:
439 | self.selectorTorque.setCurrentIndex(1)
440 | elif value == SimpleFOCDevice.FOC_CURRENT_TORQUE:
441 | self.selectorTorque.setCurrentIndex(2)
442 | self.blockSignals(False)
443 |
444 | def changeTorque(self):
445 | index = self.selectorTorque.currentIndex()
446 | if index == 0:
447 | self.device.sendTorqueType(SimpleFOCDevice.VOLTAGE_TORQUE)
448 | elif index == 1:
449 | self.device.sendTorqueType(SimpleFOCDevice.DC_CURRENT_TORQUE)
450 | elif index == 2:
451 | self.device.sendTorqueType(SimpleFOCDevice.FOC_CURRENT_TORQUE)
452 |
453 | def setEnabledDisabled(self, value):
454 | self.blockSignals(True)
455 | if value == 0:
456 | self.selectStatus.setCurrentIndex(0)
457 | elif value == 1:
458 | self.selectStatus.setCurrentIndex(1)
459 | self.blockSignals(False)
460 |
461 | def changeStatus(self):
462 | index = self.selectStatus.currentIndex()
463 | if index == 0:
464 | self.device.sendDeviceStatus(0)
465 | elif index == 1:
466 | self.device.sendDeviceStatus(1)
467 |
468 | def setModCenter(self,value):
469 | self.blockSignals(True)
470 | self.selectModCenter.setCurrentIndex(value)
471 | self.blockSignals(False)
472 |
473 | def changeModCenter(self):
474 | index = self.selectModCenter.currentIndex()
475 | if index == 0:
476 | self.device.sendModulationCentered(0)
477 | elif index == 1:
478 | self.device.sendModulationCentered(1)
479 |
480 | def setModType(self, value):
481 | self.blockSignals(True)
482 | if value == SimpleFOCDevice.SINE_PWM:
483 | self.selectModulation.setCurrentIndex(0)
484 | elif value == SimpleFOCDevice.SPACE_VECTOR_PWM:
485 | self.selectModulation.setCurrentIndex(1)
486 | elif value == SimpleFOCDevice.TRAPEZOIDAL_120:
487 | self.selectModulation.setCurrentIndex(2)
488 | elif value == SimpleFOCDevice.TRAPEZOIDAL_150:
489 | self.selectModulation.setCurrentIndex(3)
490 | self.blockSignals(False)
491 |
492 | def changeModType(self):
493 | index = self.selectModulation.currentIndex()
494 | if index == 0:
495 | self.device.sendModulationType(SimpleFOCDevice.SINE_PWM)
496 | elif index == 1:
497 | self.device.sendModulationType(SimpleFOCDevice.SPACE_VECTOR_PWM)
498 | elif index == 2:
499 | self.device.sendModulationType(SimpleFOCDevice.TRAPEZOIDAL_120)
500 | elif index == 3:
501 | self.device.sendModulationType(SimpleFOCDevice.TRAPEZOIDAL_150)
502 |
503 | def setControlLopMode(self, value):
504 | self.blockSignals(True)
505 | if value == SimpleFOCDevice.TORQUE_CONTROL:
506 | self.selectorControlLoop.setCurrentIndex(0)
507 | elif value == SimpleFOCDevice.VELOCITY_CONTROL:
508 | self.selectorControlLoop.setCurrentIndex(1)
509 | elif value == SimpleFOCDevice.ANGLE_CONTROL:
510 | self.selectorControlLoop.setCurrentIndex(2)
511 | elif value == SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL:
512 | self.selectorControlLoop.setCurrentIndex(3)
513 | elif value == SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL:
514 | self.selectorControlLoop.setCurrentIndex(4)
515 | self.blockSignals(False)
516 |
517 | def changeControlLoop(self):
518 | index = self.selectorControlLoop.currentIndex()
519 | if index == 0:
520 | self.device.sendControlType(SimpleFOCDevice.TORQUE_CONTROL)
521 | elif index == 1:
522 | self.device.sendControlType(SimpleFOCDevice.VELOCITY_CONTROL)
523 | elif index == 2:
524 | self.device.sendControlType(SimpleFOCDevice.ANGLE_CONTROL)
525 | elif index == 3:
526 | self.device.sendControlType(SimpleFOCDevice.VELOCITY_OPENLOOP_CONTROL)
527 | elif index == 4:
528 | self.device.sendControlType(SimpleFOCDevice.ANGLE_OPENLOOP_CONTROL)
529 |
530 | def connectionStateChanged(self, connectionFlag):
531 | if connectionFlag is True:
532 | self.setEnabled(True)
533 | else:
534 | self.setEnabled(False)
535 |
--------------------------------------------------------------------------------
/src/gui/configtool/devicesInspectorTree.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5.QtWidgets import (QVBoxLayout, QFrame)
4 |
5 | from src.gui.configtool.connectionControl import ConnectionControlGroupBox
6 | from src.gui.configtool.deviceJoggingControl import DeviceJoggingControl
7 | from src.gui.configtool.deviceTreeview import DeviceTreeView
8 | from src.gui.configtool.droDisplayWidget import DROGroupBox
9 | from src.gui.configtool.generalControls import GeneralControls
10 | from src.simpleFOCConnector import SimpleFOCDevice
11 |
12 |
13 | class DevicesInspectorTree(QFrame):
14 | def __init__(self, parent=None):
15 | super().__init__(parent)
16 | self.device = SimpleFOCDevice.getInstance()
17 |
18 | self.layout = QVBoxLayout(self)
19 | self.setLayout(self.layout)
20 |
21 | self.droWidget = DROGroupBox(self)
22 | self.layout.addWidget(self.droWidget)
23 |
24 | self.generalControls = GeneralControls(self)
25 | self.layout.addWidget(self.generalControls)
26 |
27 | self.treeView = DeviceTreeView(self)
28 | self.layout.addWidget(self.treeView)
29 |
30 | self.joggingControl = DeviceJoggingControl(self)
31 | self.layout.addWidget(self.joggingControl)
32 |
33 | self.connectionControl = ConnectionControlGroupBox(self)
34 | self.layout.addWidget(self.connectionControl)
35 |
36 | self.setMaximumWidth(500)
37 |
--------------------------------------------------------------------------------
/src/gui/configtool/droDisplayWidget.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | from PyQt5 import QtGui, QtWidgets
5 |
6 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
7 | from src.simpleFOCConnector import SimpleFOCDevice
8 |
9 |
10 | class DROGroupBox(QtWidgets.QGroupBox):
11 |
12 | def __init__(self, parent=None):
13 | super().__init__(parent)
14 |
15 | self.device = SimpleFOCDevice.getInstance()
16 |
17 | self.setTitle('Simple FOC 数字状态显示区')
18 | self.setObjectName('digitalReadOut')
19 |
20 | self.droHorizontalLayout = QtWidgets.QHBoxLayout(self)
21 | self.droHorizontalLayout.setObjectName('droHorizontalLayout')
22 |
23 | self.signal0Label = QtWidgets.QLabel(self)
24 | self.signal0Label.setObjectName('angleLabel')
25 | self.signal0Label.setText('角度')
26 | self.droHorizontalLayout.addWidget(self.signal0Label)
27 |
28 | self.signal0LCDNumber = QtWidgets.QLCDNumber(self)
29 | self.putStyleToLCDNumber(self.signal0LCDNumber)
30 | self.signal0LCDNumber.setObjectName('signal0LCDNumber')
31 | self.droHorizontalLayout.addWidget(self.signal0LCDNumber)
32 |
33 | self.signal1Label = QtWidgets.QLabel(self)
34 | self.signal1Label.setObjectName('velLabel')
35 | self.signal1Label.setText('速度')
36 | self.droHorizontalLayout.addWidget(self.signal1Label)
37 |
38 | self.signal1LCDNumber = QtWidgets.QLCDNumber(self)
39 | self.putStyleToLCDNumber(self.signal1LCDNumber)
40 | self.signal1LCDNumber.setObjectName('signal1LCDNumber')
41 | self.droHorizontalLayout.addWidget(self.signal1LCDNumber)
42 |
43 | self.signal2Label = QtWidgets.QLabel(self)
44 | self.signal2Label.setObjectName('torqueLabel')
45 | self.signal2Label.setText('目标')
46 | self.droHorizontalLayout.addWidget(self.signal2Label)
47 |
48 | self.signal2LCDNumber = QtWidgets.QLCDNumber(self)
49 | self.putStyleToLCDNumber(self.signal2LCDNumber)
50 | self.signal2LCDNumber.setObjectName('signal2LCDNumber')
51 | self.droHorizontalLayout.addWidget(self.signal2LCDNumber)
52 |
53 | self.signal3Label = QtWidgets.QLabel(self)
54 | self.signal3Label.setObjectName('targetLabel')
55 | self.signal3Label.setText('目标')
56 | self.droHorizontalLayout.addWidget(self.signal3Label)
57 |
58 | self.signal3LCDNumber = QtWidgets.QLCDNumber(self)
59 | self.putStyleToLCDNumber(self.signal3LCDNumber)
60 | self.signal3LCDNumber.setObjectName('signal3LCDNumber')
61 | self.droHorizontalLayout.addWidget(self.signal3LCDNumber)
62 |
63 | self.commandResponseReceived('init')
64 |
65 | self.initDiplay()
66 | self.disableUI()
67 |
68 | self.device.addConnectionStateListener(self)
69 | self.device.commProvider.stateMonitorReceived.connect(self.commandResponseReceived)
70 |
71 | self.connectionStateChanged(self.device.isConnected)
72 |
73 | def connectionStateChanged(self, isConnectedFlag):
74 | if isConnectedFlag is True:
75 | self.enabeUI()
76 | self.initDiplay()
77 | else:
78 | self.initDiplay()
79 | self.disableUI()
80 |
81 | def enabeUI(self):
82 | self.setEnabled(True)
83 |
84 | def disableUI(self):
85 | self.setEnabled(False)
86 |
87 | def initDiplay(self):
88 | self.signal0LCDNumber.display(0.0)
89 | self.signal1LCDNumber.display(0.0)
90 | self.signal2LCDNumber.display(0.0)
91 | self.signal3LCDNumber.display(0.0)
92 |
93 | def putStyleToLCDNumber(self, lcdNumber):
94 | lcdNumber.setStyleSheet('''QLCDNumber {background-color: white;}''')
95 | palette = self.setColor(lcdNumber.palette(), GUIToolKit.RED_COLOR)
96 | lcdNumber.setPalette(palette)
97 |
98 | def setColor(self, palette, colorTouple):
99 | R = colorTouple[0]
100 | G = colorTouple[1]
101 | B = colorTouple[2]
102 | # foreground color
103 | palette.setColor(palette.WindowText, QtGui.QColor(R, G, B))
104 | # background color
105 | palette.setColor(palette.Background, QtGui.QColor(R, G, B))
106 | # 'light' border
107 | palette.setColor(palette.Light, QtGui.QColor(R, G, B))
108 | # 'dark' border
109 | palette.setColor(palette.Dark, QtGui.QColor(R, G, B))
110 | return palette
111 |
112 | def commandResponseReceived(self, cmdRespose):
113 | if self.device.torqueType == SimpleFOCDevice.VOLTAGE_TORQUE:
114 | self.signal2Label.setText("电压")
115 | self.signal2LCDNumber.display(self.device.voltageQNow)
116 | else: # dc current or FOC current
117 | self.signal2Label.setText("电流")
118 | self.signal2LCDNumber.display(self.device.currentQNow)
119 |
120 | self.signal3LCDNumber.display(self.device.targetNow)
121 | self.signal1LCDNumber.display(self.device.velocityNow)
122 | self.signal0LCDNumber.display(self.device.angleNow)
123 |
--------------------------------------------------------------------------------
/src/gui/configtool/generalControls.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtGui, QtWidgets, QtCore
4 |
5 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
6 | from src.simpleFOCConnector import SimpleFOCDevice
7 |
8 |
9 | class GeneralControls(QtWidgets.QGroupBox):
10 |
11 | def __init__(self, parent=None):
12 |
13 | super().__init__(parent)
14 |
15 | # self.setMaximumWidth(300)
16 |
17 | onlyFloat = QtGui.QRegExpValidator(
18 | QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+"))
19 |
20 | self.device = SimpleFOCDevice.getInstance()
21 |
22 | self.setTitle('通用控制')
23 |
24 | self.setObjectName('generalControls')
25 |
26 | self.gcGridLayout = QtWidgets.QGridLayout(self)
27 | self.gcGridLayout.setObjectName('gcGridLayout')
28 |
29 |
30 | self.enableDeviceButton = QtWidgets.QPushButton(self)
31 | self.enableDeviceButton.setObjectName('enButton')
32 | self.enableDeviceButton.setText('使能设备')
33 | self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('greendot'))
34 | self.enableDeviceButton.clicked.connect(self.toggleEnable)
35 | self.gcGridLayout.addWidget(self.enableDeviceButton, 1, 0, 1, 1)
36 |
37 | self.sensorZeroButton = QtWidgets.QPushButton(self)
38 | self.sensorZeroButton.setObjectName('homeButton')
39 | self.sensorZeroButton.setText('位置归零')
40 | self.sensorZeroButton.setIcon(GUIToolKit.getIconByName('home'))
41 | self.sensorZeroButton.clicked.connect(self.setSensorZero)
42 | self.gcGridLayout.addWidget(self.sensorZeroButton, 1, 1, 1, 1)
43 |
44 | self.setZeroTarget = QtWidgets.QPushButton(self)
45 | self.setZeroTarget.setObjectName('zeroButton')
46 | self.setZeroTarget.setText('目标归零')
47 | self.setZeroTarget.setIcon(GUIToolKit.getIconByName('stop'))
48 | self.setZeroTarget.clicked.connect(self.setTargetZero)
49 | self.gcGridLayout.addWidget(self.setZeroTarget, 1, 2, 1, 1)
50 | self.reloadValues()
51 |
52 | self.device.addConnectionStateListener(self)
53 | self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived)
54 |
55 | self.connectionStateChanged(self.device.isConnected)
56 |
57 | def connectionStateChanged(self, deviceConnected):
58 | if deviceConnected is True:
59 | self.enabeUI()
60 | else:
61 | self.disableUI()
62 |
63 | def enabeUI(self):
64 | self.setEnabled(True)
65 |
66 | def disableUI(self):
67 | self.setEnabled(False)
68 |
69 | def setSensorZero(self):
70 | val = self.device.angleNow + self.device.sensorZeroOffset
71 | self.device.sendSensorZeroOffset(val)
72 |
73 | def setTargetZero(self):
74 | self.device.sendTargetValue(0)
75 |
76 | def toggleEnable(self):
77 | val = int( not self.device.deviceStatus)
78 | self.device.sendDeviceStatus(val)
79 |
80 | def commandResponseReceived(self, comandResponse):
81 | self.reloadValues()
82 |
83 | def reloadValues(self):
84 | if self.device.deviceStatus:
85 | self.enableDeviceButton.setText('失能设备')
86 | self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('reddot'))
87 | else:
88 | self.enableDeviceButton.setText('使能设备')
89 | self.enableDeviceButton.setIcon(GUIToolKit.getIconByName('greendot'))
--------------------------------------------------------------------------------
/src/gui/configtool/generalSettingsWidget.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtGui, QtWidgets, QtCore
4 |
5 | from src.gui.sharedcomnponets.sharedcomponets import ConfigQLineEdit
6 | from src.simpleFOCConnector import SimpleFOCDevice
7 |
8 |
9 | class GeneralSettingsGroupBox(QtWidgets.QGroupBox):
10 |
11 | def __init__(self, parent=None):
12 |
13 | super().__init__(parent)
14 |
15 | self.setMaximumWidth(300)
16 |
17 | onlyFloat = QtGui.QRegExpValidator(
18 | QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+"))
19 |
20 | self.device = SimpleFOCDevice.getInstance()
21 |
22 | self.setTitle('通用设备设置')
23 |
24 | self.setObjectName('generalDeviceSettings')
25 |
26 | self.gcGridLayout = QtWidgets.QGridLayout(self)
27 | self.gcGridLayout.setObjectName('gcGridLayout')
28 |
29 | self.motionDownsample = QtWidgets.QLabel(self)
30 | self.motionDownsample.setObjectName('motionDownsample')
31 | self.motionDownsample.setText('运动控制频率降采样')
32 | self.gcGridLayout.addWidget(self.motionDownsample, 2, 0, 1, 1)
33 |
34 | self.motionDownsampleEdit = ConfigQLineEdit(self)
35 | self.motionDownsampleEdit.setObjectName('motionDownsampleEdit')
36 | self.motionDownsampleEdit.setValidator(onlyFloat)
37 | self.motionDownsampleEdit.setAlignment(QtCore.Qt.AlignCenter)
38 | self.motionDownsampleEdit.updateValue.connect(self.sendMotionDownsampleAction)
39 | self.gcGridLayout.addWidget(self.motionDownsampleEdit, 2, 1, 1, 1)
40 |
41 | self.curLimitLabel = QtWidgets.QLabel(self)
42 | self.curLimitLabel.setObjectName('curLimitLabel')
43 | self.curLimitLabel.setText('电流限制')
44 | self.gcGridLayout.addWidget(self.curLimitLabel, 3, 0, 1, 1)
45 |
46 | self.velLimitlabel = QtWidgets.QLabel(self)
47 | self.velLimitlabel.setObjectName('velLimitlabel')
48 | self.velLimitlabel.setText('速度限制')
49 | self.gcGridLayout.addWidget(self.velLimitlabel, 4, 0, 1, 1)
50 |
51 | self.volLimitLabel = QtWidgets.QLabel(self)
52 | self.volLimitLabel.setObjectName('volLimitLabel')
53 | self.volLimitLabel.setText('电压限制')
54 | self.gcGridLayout.addWidget(self.volLimitLabel, 6, 0, 1, 1)
55 |
56 | self.clLineEdit = ConfigQLineEdit(self)
57 | self.clLineEdit.setObjectName('clLineEdit')
58 | self.clLineEdit.setValidator(onlyFloat)
59 | self.clLineEdit.setAlignment(QtCore.Qt.AlignCenter)
60 | self.clLineEdit.updateValue.connect(self.sendCurrentLimitAction)
61 | self.gcGridLayout.addWidget(self.clLineEdit, 3, 1, 1, 1)
62 |
63 | self.vlLineEdit = ConfigQLineEdit(self)
64 | self.vlLineEdit.setObjectName('vlLineEdit')
65 | self.vlLineEdit.setValidator(onlyFloat)
66 | self.vlLineEdit.setAlignment(QtCore.Qt.AlignCenter)
67 | self.vlLineEdit.updateValue.connect(self.sendVelLimitAction)
68 | self.gcGridLayout.addWidget(self.vlLineEdit, 4, 1, 1, 1)
69 |
70 | self.volLLineEdit = ConfigQLineEdit(self)
71 | self.volLLineEdit.setObjectName('volLLineEdit')
72 | self.volLLineEdit.setValidator(onlyFloat)
73 | self.volLLineEdit.setAlignment(QtCore.Qt.AlignCenter)
74 | self.volLLineEdit.updateValue.connect(self.sendVoltageLimitAction)
75 | self.gcGridLayout.addWidget(self.volLLineEdit, 6, 1, 1, 1)
76 |
77 | self.reloadValues()
78 |
79 | self.device.addConnectionStateListener(self)
80 | self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived)
81 |
82 | self.connectionStateChanged(self.device.isConnected)
83 |
84 | def connectionStateChanged(self, deviceConnected):
85 | if deviceConnected is True:
86 | self.enabeUI()
87 | else:
88 | self.disableUI()
89 |
90 | def enabeUI(self):
91 | self.setEnabled(True)
92 |
93 | def disableUI(self):
94 | self.setEnabled(False)
95 |
96 | def sendMotionDownsampleAction(self):
97 | value = self.motionDownsampleEdit.text()
98 | value = value.replace(',', '.')
99 | self.motionDownsampleEdit.setText(value)
100 | self.device.sendMotionDownsample(value)
101 |
102 | def sendCurrentLimitAction(self):
103 | value = self.clLineEdit.text()
104 | value = value.replace(',', '.')
105 | self.clLineEdit.setText(value)
106 | self.device.sendCurrentLimit(self.clLineEdit.text())
107 |
108 | def sendVelLimitAction(self):
109 | value = self.vlLineEdit.text()
110 | value = value.replace(',', '.')
111 | self.vlLineEdit.setText(value)
112 | self.device.sendVelocityLimit(self.vlLineEdit.text())
113 |
114 | def sendVoltageLimitAction(self):
115 | value = self.volLLineEdit.text()
116 | value = value.replace(',', '.')
117 | self.volLLineEdit.setText(value)
118 | self.device.sendVoltageLimit(self.volLLineEdit.text())
119 |
120 | def commandResponseReceived(self, comandResponse):
121 | self.reloadValues()
122 |
123 | def reloadValues(self):
124 | self.motionDownsampleEdit.setText(str(self.device.motionDownsample))
125 | self.clLineEdit.setText(str(self.device.currentLimit))
126 | self.vlLineEdit.setText(str(self.device.velocityLimit))
127 | self.volLLineEdit.setText(str(self.device.voltageLimit))
--------------------------------------------------------------------------------
/src/gui/configtool/generatedCodeDisplay.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 | from PyQt5.QtCore import *
5 | from PyQt5.QtCore import Qt
6 | from PyQt5.QtGui import *
7 | from PyQt5.QtWidgets import (QVBoxLayout)
8 |
9 | from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget,
10 | GUIToolKit)
11 | from src.simpleFOCConnector import SimpleFOCDevice
12 |
13 |
14 | class GenerateCodeDialog(QtWidgets.QDialog):
15 | def __init__(self, parent=None):
16 | super().__init__(parent=parent)
17 | self.setWindowTitle("Generate Code")
18 | self.setWindowIcon(GUIToolKit.getIconByName('gen'))
19 |
20 | self.checkBoxMotionControl = QtWidgets.QCheckBox(self)
21 | self.checkBoxMotionControl.setObjectName('motion')
22 | self.checkBoxMotionControl.setText("力矩/运动 控制")
23 | self.checkBoxMotionControl.setIcon(GUIToolKit.getIconByName('motor'))
24 | self.checkBoxMotionControl.setChecked(True)
25 |
26 | self.checkBoxPidVel = QtWidgets.QCheckBox(self)
27 | self.checkBoxPidVel.setObjectName('pidVel')
28 | self.checkBoxPidVel.setText("速度 PID")
29 | self.checkBoxPidVel.setIcon(GUIToolKit.getIconByName('pidconfig'))
30 | self.checkBoxPidVel.setChecked(True)
31 |
32 | self.checkBoxPidAngle = QtWidgets.QCheckBox(self)
33 | self.checkBoxPidAngle.setObjectName('pidAngle')
34 | self.checkBoxPidAngle.setText("角度 PID")
35 | self.checkBoxPidAngle.setIcon(GUIToolKit.getIconByName('pidconfig'))
36 | self.checkBoxPidAngle.setChecked(True)
37 |
38 | self.checkBoxPidCq = QtWidgets.QCheckBox(self)
39 | self.checkBoxPidCq.setObjectName('pidCq')
40 | self.checkBoxPidCq.setText("电流q PID")
41 | self.checkBoxPidCq.setIcon(GUIToolKit.getIconByName('pidconfig'))
42 | self.checkBoxPidCq.setChecked(True)
43 |
44 | self.checkBoxPidCd = QtWidgets.QCheckBox(self)
45 | self.checkBoxPidCd.setObjectName('pidCq')
46 | self.checkBoxPidCd.setText("电流d PID")
47 | self.checkBoxPidCd.setIcon(GUIToolKit.getIconByName('pidconfig'))
48 | self.checkBoxPidCd.setChecked(True)
49 |
50 | self.checkBoxLimits = QtWidgets.QCheckBox(self)
51 | self.checkBoxLimits.setObjectName('limits')
52 | self.checkBoxLimits.setText("限幅")
53 | self.checkBoxLimits.setIcon(GUIToolKit.getIconByName('statistics'))
54 | self.checkBoxLimits.setChecked(True)
55 |
56 | self.checkBoxPhaseRes = QtWidgets.QCheckBox(self)
57 | self.checkBoxPhaseRes.setObjectName('phaseRes')
58 | self.checkBoxPhaseRes.setText("相电阻")
59 | self.checkBoxPhaseRes.setIcon(GUIToolKit.getIconByName('res'))
60 | self.checkBoxPhaseRes.setChecked(True)
61 |
62 | self.checkBoxModulation = QtWidgets.QCheckBox(self)
63 | self.checkBoxModulation.setObjectName('modulation')
64 | self.checkBoxModulation.setText("PWM调制")
65 | self.checkBoxModulation.setIcon(GUIToolKit.getIconByName('gear'))
66 | self.checkBoxModulation.setChecked(True)
67 |
68 | self.sensorOffset = QtWidgets.QCheckBox(self)
69 | self.sensorOffset.setObjectName('sensorOffset')
70 | self.sensorOffset.setText("零点偏置")
71 | self.sensorOffset.setIcon(GUIToolKit.getIconByName('gear'))
72 | self.sensorOffset.setChecked(True)
73 |
74 | self.sensorElOffset = QtWidgets.QCheckBox(self)
75 | self.sensorElOffset.setObjectName('sensorOffset')
76 | self.sensorElOffset.setToolTip('仅适用于绝对编码器')
77 | self.sensorElOffset.setText("电气零点偏置")
78 | self.sensorElOffset.setIcon(GUIToolKit.getIconByName('gear'))
79 | self.sensorElOffset.setChecked(False)
80 |
81 | QBtn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
82 |
83 | self.buttonBox = QtWidgets.QDialogButtonBox(QBtn)
84 | self.buttonBox.accepted.connect(self.accept)
85 | self.buttonBox.rejected.connect(self.reject)
86 |
87 | text = "Arduino Code Generation
"
88 | text += "Arduino code generation for the motor parameters currently used in the SimpleFOCStudio
"
89 | text += "Once you are happy with the performance of your system you can generate the arduino code of the parameters you have tuned.
"
90 | text += "The generated code you can just copy/paste in your setup()
function, just before calling the motor.init()
"
91 | text += "Choose the parameter sets to be generated:
"
92 |
93 | self.layout = QtWidgets.QVBoxLayout()
94 | message1 = QtWidgets.QLabel(text)
95 | self.layout.addWidget(message1)
96 | self.layout.addWidget(self.checkBoxMotionControl)
97 | self.layout.addWidget(self.checkBoxPidVel)
98 | self.layout.addWidget(self.checkBoxPidAngle)
99 | self.layout.addWidget(self.checkBoxPidCq)
100 | self.layout.addWidget(self.checkBoxPidCd)
101 | self.layout.addWidget(self.checkBoxLimits)
102 | self.layout.addWidget(self.checkBoxPhaseRes)
103 | self.layout.addWidget(self.checkBoxModulation)
104 | self.layout.addWidget(self.sensorOffset)
105 | self.layout.addWidget(self.sensorElOffset)
106 | self.layout.addWidget(self.buttonBox)
107 | self.setLayout(self.layout)
108 |
109 | class GeneratedCodeDisplay(WorkAreaTabWidget):
110 |
111 | def __init__(self, parent=None ):
112 | super().__init__(parent)
113 |
114 | self.device = SimpleFOCDevice.getInstance()
115 | dlg = GenerateCodeDialog() # If you pass self, the dialog will be centered over the main window as before.
116 | if dlg.exec_():
117 | toGenerate=[
118 | dlg.checkBoxMotionControl.isChecked(),
119 | dlg.checkBoxPidVel.isChecked(),
120 | dlg.checkBoxPidAngle.isChecked(),
121 | dlg.checkBoxPidCq.isChecked(),
122 | dlg.checkBoxPidCd.isChecked(),
123 | dlg.checkBoxLimits.isChecked(),
124 | dlg.sensorOffset.isChecked(),
125 | dlg.sensorElOffset.isChecked(),
126 | dlg.checkBoxPhaseRes.isChecked(),
127 | dlg.checkBoxModulation.isChecked(),
128 | ]
129 | code = self.device.toArduinoCode(toGenerate)
130 |
131 | self.layout = QVBoxLayout(self)
132 |
133 | text = "Generated Arduino Code
"
134 | 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()
"
135 |
136 | message1 = QtWidgets.QLabel(text)
137 | self.layout.addWidget(message1)
138 |
139 | self.codeDisplayBefore = QtWidgets.QLabel(self)
140 | self.layout.addWidget(self.codeDisplayBefore)
141 | code0 = '#include <SimpleFOC.h>'
142 | code0 +="
...
"
143 | code0 += 'void setup() {
'
144 | code0 += " ...."
145 | self.codeDisplayBefore.setText(code0)
146 | self.codeDisplayBefore.setTextFormat(Qt.TextFormat.RichText)
147 |
148 | self.codeDisplay = QtWidgets.QTextEdit(self)
149 | self.codeDisplay.setObjectName('codeDisplay')
150 | self.codeDisplay.setText(code)
151 |
152 | highlighter = MyHighlighter( self.codeDisplay, "Classic" )
153 | self.layout.addWidget(self.codeDisplay)
154 |
155 |
156 |
157 | self.codeDisplayAfter = QtWidgets.QLabel(self)
158 | self.layout.addWidget(self.codeDisplayAfter)
159 | code1 = '// initialize motor
'
160 | code1 += 'motor.init();
'
161 | code1 += '// align sensor and start FOC
'
162 | code1 += 'motor.initFOC();
...
}'
163 | code1 += '
void loop() {
....
}'
164 | # MyHighlighter( self.codeDisplayAfter, "Classic" )
165 | self.codeDisplayAfter.setText(code1)
166 | self.codeDisplayAfter.setTextFormat(Qt.TextFormat.RichText)
167 |
168 | self.setLayout(self.layout)
169 |
170 | def getTabIcon(self):
171 | return GUIToolKit.getIconByName('gen')
172 |
173 | def getTabName(self):
174 | return 'Generated Code'
175 |
176 |
177 | class MyHighlighter( QSyntaxHighlighter ):
178 |
179 | def __init__( self, parent, theme ):
180 | QSyntaxHighlighter.__init__( self, parent )
181 | self.parent = parent
182 | keyword = QTextCharFormat()
183 | comment = QTextCharFormat()
184 |
185 | self.highlightingRules = []
186 |
187 | # keyword
188 | brush = QBrush( QColor(211,84,0), Qt.SolidPattern )
189 | keyword.setForeground( brush )
190 | keywords = list( [ "motor",'FOCModulationType','MotionControlType','TorqueControlType'] )
191 | for word in keywords:
192 | pattern = QRegExp("\\b" + word + "\\b")
193 | rule = HighlightingRule( pattern, keyword )
194 | self.highlightingRules.append( rule )
195 |
196 | # comment
197 | brush = QBrush( Qt.gray, Qt.SolidPattern )
198 | pattern = QRegExp( "\/\/.*[^\n]" )
199 | comment.setForeground( brush )
200 | comment.setFontItalic( True )
201 | rule = HighlightingRule( pattern, comment )
202 | self.highlightingRules.append( rule )
203 |
204 |
205 | def highlightBlock( self, text ):
206 | for rule in self.highlightingRules:
207 | expression = QRegExp( rule.pattern )
208 | index = expression.indexIn( text )
209 | while index >= 0:
210 | length = expression.matchedLength()
211 | self.setFormat( index, length, rule.format )
212 | index = expression.indexIn( text, index + length )
213 | self.setCurrentBlockState( 0 )
214 |
215 | class HighlightingRule():
216 | def __init__( self, pattern, format ):
217 | self.pattern = pattern
218 | self.format = format
219 |
--------------------------------------------------------------------------------
/src/gui/configtool/graphicWidget.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import logging
4 |
5 | import numpy as np
6 | import pyqtgraph as pg
7 | from PyQt5 import QtWidgets
8 |
9 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
10 | from src.simpleFOCConnector import SimpleFOCDevice
11 |
12 |
13 | class SimpleFOCGraphicWidget(QtWidgets.QGroupBox):
14 | disconnectedState = 0
15 | initialConnectedState = 1
16 | connectedPausedState = 2
17 | connectedPlottingStartedState = 3
18 |
19 |
20 | signals = ['Target', 'Vq','Vd','Cq','Cd','Vel','Angle']
21 | signal_tooltip = ['目标', '电压 Q [V]','电压 D [V]','电流 Q [mA]','电流 D [mA]','速度 [rad/sec]','角度 [rad]']
22 | signalColors = [GUIToolKit.RED_COLOR, GUIToolKit.BLUE_COLOR, GUIToolKit.PURPLE_COLOR,GUIToolKit.YELLOW_COLOR, GUIToolKit.MAROON_COLOR, GUIToolKit.ORANGE_COLOR, GUIToolKit.GREEN_COLOR]
23 | signalIcons = ['reddot', 'bluedot','purpledot', 'yellowdot', 'maroondot', 'orangedot', 'greendot']
24 |
25 | def __init__(self, parent=None):
26 |
27 | super().__init__(parent)
28 |
29 | self.setObjectName('plotWidget')
30 | self.setTitle('实时电机数据: ')
31 | self.horizontalLayout = QtWidgets.QVBoxLayout(self)
32 | self.device = SimpleFOCDevice.getInstance()
33 |
34 | self.numberOfSamples = 300
35 |
36 | pg.setConfigOptions(antialias=True)
37 | self.plotWidget = pg.PlotWidget()
38 | self.plotWidget.showGrid(x=True, y=True, alpha=0.5)
39 | self.plotWidget.addLegend()
40 |
41 | # self.legend = pg.LegendItem()
42 | # self.legend.setParentItem(self.plotWidget)
43 |
44 | self.timeArray = np.arange(-self.numberOfSamples, 0, 1)
45 |
46 | self.controlPlotWidget = ControlPlotPanel(controllerPlotWidget=self)
47 |
48 | self.signalDataArrays = []
49 | self.signalPlots = []
50 | self.signalPlotFlags = []
51 | for (sig, sigColor, checkBox, tooltip) in zip(self.signals, self.signalColors,self.controlPlotWidget.signalCheckBox, self.signal_tooltip):
52 | # define signal plot data array
53 | self.signalDataArrays.append(np.zeros(self.numberOfSamples))
54 | # configure signal plot parameters
55 | signalPen = pg.mkPen(color=sigColor, width=1.5)
56 | self.signalPlots.append(pg.PlotDataItem(self.timeArray,
57 | self.signalDataArrays[-1],
58 | pen=signalPen, name=tooltip))
59 | self.plotWidget.addItem(self.signalPlots[-1])
60 |
61 | # is plotted flag
62 | self.signalPlotFlags.append(True)
63 | # add callback
64 | checkBox.stateChanged.connect(self.signalPlotFlagUpdate)
65 |
66 |
67 | self.horizontalLayout.addWidget(self.plotWidget)
68 | self.horizontalLayout.addWidget(self.controlPlotWidget)
69 |
70 | self.device.commProvider.monitoringDataReceived.connect(
71 | self.upDateGraphic)
72 |
73 | self.currentStatus = self.disconnectedState
74 | self.controlPlotWidget.pauseContinueButton.setDisabled(True)
75 |
76 | self.device.addConnectionStateListener(self)
77 |
78 | self.connectionStateChanged(self.device.isConnected)
79 |
80 | def connectionStateChanged(self, deviceConnected):
81 | if deviceConnected is True:
82 | self.currentStatus = self.initialConnectedState
83 | self.enabeUI()
84 | else:
85 | self.controlPlotWidget.startStoPlotAction()
86 | self.controlPlotWidget.stopAndResetPlot()
87 | self.currentStatus = self.disconnectedState
88 | self.disableUI()
89 |
90 | def enabeUI(self):
91 | self.setEnabled(True)
92 |
93 | def disableUI(self):
94 | self.setEnabled(False)
95 |
96 | def signalPlotFlagUpdate(self):
97 | self.controlPlotWidget.updateMonitorVariables()
98 | for i, (checkBox, plotFlag) in enumerate(zip(self.controlPlotWidget.signalCheckBox, self.signalPlotFlags)):
99 | if checkBox.isChecked() and (not plotFlag):
100 | self.signalPlotFlags[i] = True
101 | self.plotWidget.addItem( self.signalPlots[i] )
102 | elif (not checkBox.isChecked()) and plotFlag:
103 | self.signalPlotFlags[i] = False
104 | self.plotWidget.removeItem( self.signalPlots[i] )
105 |
106 | def connectioStatusUpdate(self, connectedFlag):
107 | if connectedFlag:
108 | self.currentStatus = self.initialConnectedState
109 | else:
110 | self.currentStatus = self.disconnectedState
111 |
112 | def upDateGraphic(self, signalList):
113 | if self.currentStatus is self.connectedPlottingStartedState or \
114 | self.currentStatus is self.connectedPausedState:
115 |
116 | signals = np.array(signalList, dtype=float)
117 | signalIndex = 0
118 |
119 | enabled = np.where(np.array(self.signalPlotFlags) == True)[0]
120 |
121 | if(len(enabled) != len(signals)):
122 | logging.warning('Arrived corrupted data')
123 | return
124 | else:
125 | for i, ind in enumerate(enabled):
126 | self.signalDataArrays[ind] = np.roll(self.signalDataArrays[ind], -1)
127 | self.signalDataArrays[ind][-1] = signals[i]
128 |
129 | if self.currentStatus is self.connectedPlottingStartedState:
130 | self.updatePlot()
131 |
132 |
133 | def computeStatic(self, array):
134 | mean = np.mean(array)
135 | std = np.std(array)
136 | max = np.max(array)
137 | min = np.min(array)
138 | meadian = np.median(array)
139 |
140 | def updatePlot(self):
141 | for i, plotFlag in enumerate(self.signalPlotFlags):
142 | if plotFlag:
143 | self.signalPlots[i].setData(self.timeArray, self.signalDataArrays[i])
144 | self.signalPlots[i].updateItems()
145 | self.signalPlots[i].sigPlotChanged.emit(self.signalPlots[i])
146 |
147 |
148 | class ControlPlotPanel(QtWidgets.QWidget):
149 |
150 | def __init__(self, parent=None, controllerPlotWidget=None):
151 | '''Constructor for ToolsWidget'''
152 | super().__init__(parent)
153 |
154 | self.device = SimpleFOCDevice.getInstance()
155 | self.controlledPlot = controllerPlotWidget
156 |
157 | self.verticalLayout = QtWidgets.QVBoxLayout(self)
158 | self.setLayout(self.verticalLayout)
159 |
160 | self.horizontalLayout1 = QtWidgets.QHBoxLayout()
161 | self.horizontalLayout1.setObjectName('horizontalLayout')
162 |
163 | self.startStopButton = QtWidgets.QPushButton(self)
164 | self.startStopButton.setText('开始')
165 | self.startStopButton.setObjectName('Start')
166 | self.startStopButton.clicked.connect(self.startStoPlotAction)
167 | self.startStopButton.setIcon(GUIToolKit.getIconByName('start'))
168 | self.horizontalLayout1.addWidget(self.startStopButton)
169 |
170 | self.pauseContinueButton = QtWidgets.QPushButton(self)
171 | self.pauseContinueButton.setObjectName('pauseButton')
172 | self.pauseContinueButton.setText('暂停')
173 | self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause'))
174 | self.pauseContinueButton.clicked.connect(self.pauseContinuePlotAction)
175 | self.horizontalLayout1.addWidget(self.pauseContinueButton)
176 |
177 | self.zoomAllButton = QtWidgets.QPushButton(self)
178 | self.zoomAllButton.setObjectName('zoomAllButton')
179 | self.zoomAllButton.setText('显示所有')
180 | self.zoomAllButton.setIcon(GUIToolKit.getIconByName('zoomall'))
181 | self.zoomAllButton.clicked.connect(self.zoomAllPlot)
182 | self.horizontalLayout1.addWidget(self.zoomAllButton)
183 |
184 | self.signalCheckBox = []
185 | for i in range(len(self.controlledPlot.signals)):
186 | checkBox = QtWidgets.QCheckBox(self)
187 | checkBox.setObjectName('signalCheckBox'+str(i))
188 | checkBox.setToolTip(self.controlledPlot.signal_tooltip[i])
189 | checkBox.setText(self.controlledPlot.signals[i])
190 | checkBox.setIcon(GUIToolKit.getIconByName(self.controlledPlot.signalIcons[i]))
191 | checkBox.setChecked(True)
192 | self.signalCheckBox.append(checkBox)
193 | self.horizontalLayout1.addWidget(checkBox)
194 |
195 |
196 | spacerItem = QtWidgets.QSpacerItem(100, 20,
197 | QtWidgets.QSizePolicy.Expanding,
198 | QtWidgets.QSizePolicy.Maximum)
199 |
200 | self.horizontalLayout1.addItem(spacerItem)
201 | self.horizontalLayout1.addItem(spacerItem)
202 |
203 | self.downsampleLabel = QtWidgets.QLabel(self)
204 | self.downsampleLabel.setText('降采样')
205 | self.downampleValue = QtWidgets.QLineEdit(self.downsampleLabel)
206 | self.downampleValue.setText("100")
207 | self.downampleValue.editingFinished.connect(self.changeDownsampling)
208 | self.horizontalLayout1.addWidget(self.downsampleLabel)
209 | self.horizontalLayout1.addWidget(self.downampleValue)
210 |
211 | self.verticalLayout.addLayout(self.horizontalLayout1)
212 |
213 | def startStoPlotAction(self):
214 | if self.controlledPlot.currentStatus is self.controlledPlot.initialConnectedState:
215 | # Start pressed
216 | self.startStopButton.setText('停止')
217 | self.startStopButton.setIcon(GUIToolKit.getIconByName('stop'))
218 | self.controlledPlot.currentStatus = \
219 | self.controlledPlot.connectedPlottingStartedState
220 | self.pauseContinueButton.setEnabled(True)
221 | self.device.sendMonitorDownsample(int(self.downampleValue.text()))
222 | self.updateMonitorVariables()
223 | else:
224 | # Stop pressed
225 | self.startStopButton.setText('开始')
226 | self.startStopButton.setIcon(GUIToolKit.getIconByName('start'))
227 | self.pauseContinueButton.setText('暂停')
228 | self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause'))
229 | self.pauseContinueButton.setEnabled(False)
230 | self.stopAndResetPlot()
231 | self.device.sendMonitorDownsample(0)
232 | self.device.sendMonitorClearVariables()
233 |
234 | def pauseContinuePlotAction(self):
235 | if self.controlledPlot.currentStatus is self.controlledPlot.connectedPausedState:
236 | # Continue pressed
237 | self.pauseContinueButton.setText('停止')
238 | self.pauseContinueButton.setIcon(GUIToolKit.getIconByName('pause'))
239 | self.controlledPlot.currentStatus = self.controlledPlot.connectedPlottingStartedState
240 | else:
241 | # Pause pressed
242 | self.pauseContinueButton.setText('继续')
243 | self.pauseContinueButton.setIcon(
244 | GUIToolKit.getIconByName('continue'))
245 | self.controlledPlot.currentStatus = self.controlledPlot.connectedPausedState
246 |
247 | def stopAndResetPlot(self):
248 | self.controlledPlot.currentStatus = self.controlledPlot.initialConnectedState
249 | for dataArray in self.controlledPlot.signalDataArrays:
250 | dataArray = np.zeros(self.controlledPlot.numberOfSamples)
251 |
252 | def zoomAllPlot(self):
253 | self.controlledPlot.plotWidget.enableAutoRange()
254 |
255 | def changeDownsampling(self):
256 | if self.controlledPlot.currentStatus == self.controlledPlot.connectedPlottingStartedState:
257 | self.device.sendMonitorDownsample(int(self.downampleValue.text()))
258 |
259 | def updateMonitorVariables(self):
260 | self.device.sendMonitorVariables([self.signalCheckBox[0].isChecked(),
261 | self.signalCheckBox[1].isChecked(),
262 | self.signalCheckBox[2].isChecked(),
263 | self.signalCheckBox[3].isChecked(),
264 | self.signalCheckBox[4].isChecked(),
265 | self.signalCheckBox[5].isChecked(),
266 | self.signalCheckBox[6].isChecked()])
--------------------------------------------------------------------------------
/src/gui/configtool/pidConfiguration.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtGui, QtWidgets, QtCore
4 |
5 | from src.gui.sharedcomnponets.sharedcomponets import ConfigQLineEdit
6 | from src.simpleFOCConnector import SimpleFOCDevice
7 |
8 |
9 | class PidGroupBox(QtWidgets.QGroupBox):
10 |
11 | def __init__(self, parent=None):
12 |
13 | super().__init__(parent)
14 | self.setMaximumWidth(300)
15 |
16 | onlyFloatInputValidator = QtGui.QRegExpValidator(
17 | QtCore.QRegExp("[+-]?([0-9]*[.])?[0-9]+"))
18 |
19 | self.device = SimpleFOCDevice.getInstance()
20 |
21 |
22 | self.activePID = self.device.PIDVelocity
23 | self.activeLPF = self.device.LPFVelocity
24 |
25 | self.setObjectName('pidConfigurator')
26 | self.setTitle('PID控制器设置')
27 |
28 | self.gridLayout = QtWidgets.QGridLayout(self)
29 | self.gridLayout.setObjectName('gridLayout')
30 |
31 | self.sPidLabel = QtWidgets.QLabel(self)
32 | self.sPidLabel.setObjectName('pgLabel')
33 | self.sPidLabel.setText('选择PID模式')
34 | self.gridLayout.addWidget(self.sPidLabel, 0, 1, 1, 1)
35 |
36 | self.selectorPIDF = QtWidgets.QComboBox(self)
37 | self.selectorPIDF.setObjectName('selectPIDF')
38 | self.selectorPIDF.addItems(['速度', '角度', '电流 Q', '电流 D'])
39 | self.selectorPIDF.currentIndexChanged.connect(self.changePIDF)
40 | self.gridLayout.addWidget(self.selectorPIDF, 0, 2, 1, 1)
41 |
42 | self.pgLabel = QtWidgets.QLabel(self)
43 | self.pgLabel.setObjectName('pgLabel')
44 | self.pgLabel.setText('P 比例项')
45 | self.gridLayout.addWidget(self.pgLabel, 1, 1, 1, 1)
46 |
47 | self.iglabel = QtWidgets.QLabel(self)
48 | self.iglabel.setObjectName('iglabel')
49 | self.iglabel.setText('I 积分项')
50 | self.gridLayout.addWidget(self.iglabel, 2, 1, 1, 1)
51 |
52 | self.dgLabel = QtWidgets.QLabel(self)
53 | self.dgLabel.setObjectName('dgLabel')
54 | self.dgLabel.setText('D 微分项')
55 | self.gridLayout.addWidget(self.dgLabel, 3, 1, 1, 1)
56 |
57 | self.vrLabel = QtWidgets.QLabel(self)
58 | self.vrLabel.setObjectName('vrLabel')
59 | self.vrLabel.setText('斜坡输出')
60 | self.gridLayout.addWidget(self.vrLabel, 4, 1, 1, 1)
61 |
62 | self.vrLabel = QtWidgets.QLabel(self)
63 | self.vrLabel.setObjectName('lpfLabel')
64 | self.vrLabel.setText('低通滤波器')
65 | self.gridLayout.addWidget(self.vrLabel, 5, 1, 1, 1)
66 |
67 | self.pgLineEdit = ConfigQLineEdit(self)
68 | self.pgLineEdit.setObjectName('pgLineEdit')
69 | self.pgLineEdit.setValidator(onlyFloatInputValidator)
70 | self.pgLineEdit.setAlignment(QtCore.Qt.AlignCenter)
71 | self.pgLineEdit.editingFinished.connect(self.sendProportionalGainAction)
72 | self.gridLayout.addWidget(self.pgLineEdit, 1, 2, 1, 1)
73 |
74 | self.igLineEdit = ConfigQLineEdit(self)
75 | self.igLineEdit.setObjectName('igLineEdit')
76 | self.igLineEdit.setValidator(onlyFloatInputValidator)
77 | self.igLineEdit.setAlignment(QtCore.Qt.AlignCenter)
78 | self.igLineEdit.editingFinished.connect(self.sendIntegralGainAction)
79 | self.gridLayout.addWidget(self.igLineEdit, 2, 2, 1, 1)
80 |
81 | self.dgLineEdit = ConfigQLineEdit(self)
82 | self.dgLineEdit.setObjectName('dgLineEdit')
83 | self.dgLineEdit.setValidator(onlyFloatInputValidator)
84 | self.dgLineEdit.setAlignment(QtCore.Qt.AlignCenter)
85 | self.dgLineEdit.editingFinished.connect(self.sendDerivativeGainAction)
86 | self.gridLayout.addWidget(self.dgLineEdit, 3, 2, 1, 1)
87 |
88 | self.vrLineEdit = ConfigQLineEdit(self)
89 | self.vrLineEdit.setObjectName('vrLineEdit')
90 | self.vrLineEdit.setValidator(onlyFloatInputValidator)
91 | self.vrLineEdit.setAlignment(QtCore.Qt.AlignCenter)
92 | self.vrLineEdit.editingFinished.connect(self.sendRampAction)
93 | self.gridLayout.addWidget(self.vrLineEdit, 4, 2, 1, 1)
94 |
95 | self.lpfLineEdit = ConfigQLineEdit(self)
96 | self.lpfLineEdit.setObjectName('lpfLineEdit')
97 | self.lpfLineEdit.setValidator(onlyFloatInputValidator)
98 | self.lpfLineEdit.setAlignment(QtCore.Qt.AlignCenter)
99 | self.lpfLineEdit.editingFinished.connect(self.sendLPFAction)
100 | self.gridLayout.addWidget(self.lpfLineEdit, 5, 2, 1, 1)
101 |
102 | self.reloadPIDValues()
103 |
104 | # self.setDerivativeGainAction(self.device.derivativeGainPID)
105 | # self.setProportionalGainAction(self.device.proportionalGainPID)
106 | # self.setIntegralGainAction(self.device.integralGainPID)
107 | # self.setVoltageRampAction(self.device.voltageRampPID)
108 |
109 | self.connectionStateChanged(self.device.isConnected)
110 | self.device.addConnectionStateListener(self)
111 | self.device.commProvider.commandDataReceived.connect(self.commandResponseReceived)
112 |
113 | def connectionStateChanged(self, deviceConnected):
114 | if deviceConnected is True:
115 | self.enabeUI()
116 | else:
117 | self.disableUI()
118 |
119 | def enabeUI(self):
120 | self.setEnabled(True)
121 |
122 | def disableUI(self):
123 | self.setEnabled(False)
124 |
125 | def sendDerivativeGainAction(self):
126 | value = self.dgLineEdit.text()
127 | value = value.replace(',', '.')
128 | self.dgLineEdit.setText(value)
129 | self.device.sendDerivativeGain(self.activePID, value)
130 |
131 | def sendProportionalGainAction(self):
132 | value = self.pgLineEdit.text()
133 | value = value.replace(',', '.')
134 | self.pgLineEdit.setText(value)
135 | self.device.sendProportionalGain(self.activePID, self.pgLineEdit.text())
136 |
137 | def sendIntegralGainAction(self):
138 | value = self.igLineEdit.text()
139 | value = value.replace(',', '.')
140 | self.igLineEdit.setText(value)
141 | self.device.sendIntegralGain(self.activePID, self.igLineEdit.text())
142 |
143 | def sendRampAction(self):
144 | value = self.vrLineEdit.text()
145 | value = value.replace(',', '.')
146 | self.vrLineEdit.setText(value)
147 | self.device.sendOutputRamp(self.activePID, self.vrLineEdit.text())
148 |
149 | def sendLPFAction(self):
150 | value = self.lpfLineEdit.text()
151 | value = value.replace(',', '.')
152 | self.lpfLineEdit.setText(value)
153 | self.device.sendLowPassFilter(self.activeLPF, self.lpfLineEdit.text())
154 |
155 | def reloadPIDValues(self):
156 | self.pgLineEdit.setText(str(self.activePID.P))
157 | self.dgLineEdit.setText(str(self.activePID.D))
158 | self.igLineEdit.setText(str(self.activePID.I))
159 | self.vrLineEdit.setText(str(self.activePID.outputRamp))
160 | self.lpfLineEdit.setText(str(self.activeLPF.Tf))
161 |
162 | def changePIDF(self):
163 | index = self.selectorPIDF.currentIndex()
164 | if index == 0:
165 | self.activePID = self.device.PIDVelocity
166 | self.activeLPF = self.device.LPFVelocity
167 | elif index == 1:
168 | self.activePID = self.device.PIDAngle
169 | self.activeLPF = self.device.LPFAngle
170 | elif index == 2:
171 | self.activePID = self.device.PIDCurrentQ
172 | self.activeLPF = self.device.LPFCurrentQ
173 | elif index == 3:
174 | self.activePID = self.device.PIDCurrentD
175 | self.activeLPF = self.device.LPFCurrentD
176 |
177 | self.device.pullPIDConf(self.activePID, self.activeLPF)
178 | self.reloadPIDValues()
179 |
180 | def commandResponseReceived(self, commandDataReceived):
181 | self.pgLineEdit.setText(str(self.activePID.P))
182 | self.dgLineEdit.setText(str(self.activePID.D))
183 | self.igLineEdit.setText(str(self.activePID.I))
184 | self.vrLineEdit.setText(str(self.activePID.outputRamp))
185 | self.lpfLineEdit.setText(str(self.activeLPF.Tf))
186 |
187 |
--------------------------------------------------------------------------------
/src/gui/configtool/torqueConfig.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 |
5 | from src.simpleFOCConnector import SimpleFOCDevice
6 |
7 |
8 | class TorqueGroupBox(QtWidgets.QGroupBox):
9 | def __init__(self, parent=None):
10 | super().__init__(parent)
11 |
12 | self.device = SimpleFOCDevice.getInstance()
13 |
14 | self.setObjectName('torqueMode')
15 | self.setTitle('力矩模式')
16 |
17 | self.torqueTypeHorizontalLayout = QtWidgets.QHBoxLayout(self)
18 | self.torqueTypeHorizontalLayout.setObjectName('torqueHorizontalLayout')
19 |
20 |
21 |
22 | self.selectorTorque = QtWidgets.QComboBox(self)
23 | self.selectorTorque.setObjectName('selectorControlLoop')
24 | self.selectorTorque.addItems(['电压', 'DC电流', 'FOC电流'])
25 | self.selectorTorque.currentIndexChanged.connect(self.changeTorque)
26 | self.torqueTypeHorizontalLayout.addWidget(self.selectorTorque)
27 |
28 | self.setTorqueMode(self.device.torqueType)
29 |
30 | self.disableUI()
31 | self.device.addConnectionStateListener(self)
32 | self.device.commProvider.commandDataReceived.connect(
33 | self.commandResponseReceived)
34 |
35 | self.connectionStateChanged(self.device.isConnected)
36 |
37 | def connectionStateChanged(self, deviceConnected):
38 | if deviceConnected is True:
39 | self.enabeUI()
40 | else:
41 | self.disableUI()
42 |
43 | def enabeUI(self):
44 | self.setEnabled(True)
45 |
46 | def disableUI(self):
47 | self.setEnabled(False)
48 |
49 | def setTorqueMode(self, value):
50 | if value == SimpleFOCDevice.VOLTAGE_TORQUE:
51 | self.selectorTorque.setCurrentIndex(0)
52 | elif value == SimpleFOCDevice.DC_CURRENT_TORQUE:
53 | self.selectorTorque.setCurrentIndex(1)
54 | elif value == SimpleFOCDevice.FOC_CURRENT_TORQUE:
55 | self.selectorTorque.setCurrentIndex(2)
56 |
57 | def changeTorque(self):
58 | index = self.selectorTorque.currentIndex()
59 | if index == 0:
60 | self.device.sendTorqueType(SimpleFOCDevice.VOLTAGE_TORQUE)
61 | elif index == 1:
62 | self.device.sendTorqueType(SimpleFOCDevice.DC_CURRENT_TORQUE)
63 | elif index == 2:
64 | self.device.sendTorqueType(SimpleFOCDevice.FOC_CURRENT_TORQUE)
65 |
66 | def commandResponseReceived(self, cmdRespose):
67 | self.setTorqueMode(self.device.torqueType)
--------------------------------------------------------------------------------
/src/gui/configtool/treeViewConfigTool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5.QtCore import Qt
4 | from PyQt5.QtWidgets import (QVBoxLayout, QSplitter)
5 |
6 | from src.gui.configtool.deviceInteractionFrame import DeviceInteractionFrame
7 | from src.gui.configtool.devicesInspectorTree import DevicesInspectorTree
8 | from src.gui.sharedcomnponets.sharedcomponets import (WorkAreaTabWidget,
9 | GUIToolKit)
10 | from src.simpleFOCConnector import SimpleFOCDevice
11 |
12 |
13 | class TreeViewConfigTool(WorkAreaTabWidget):
14 |
15 | def __init__(self, parent=None):
16 | super().__init__(parent)
17 | self.device = SimpleFOCDevice.getInstance()
18 |
19 | self.layout = QVBoxLayout(self)
20 |
21 | self.treeViewWidget = DevicesInspectorTree(self)
22 | self.leftWidget = DeviceInteractionFrame(self)
23 |
24 | self.verticalSplitter = QSplitter(Qt.Horizontal)
25 | self.verticalSplitter.addWidget(self.treeViewWidget)
26 | self.verticalSplitter.addWidget(self.leftWidget)
27 |
28 | self.layout.addWidget(self.verticalSplitter)
29 |
30 | self.setLayout(self.layout)
31 |
32 | def getTabIcon(self):
33 | return GUIToolKit.getIconByName('motor')
34 |
35 | def getTabName(self):
36 | return self.device.connectionID
--------------------------------------------------------------------------------
/src/gui/mainWindow.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtCore, QtWidgets
4 |
5 | from src.gui.toolbar import SimpleFOCConfigToolBar
6 | from src.gui.workAreaTabbedWidget import WorkAreaTabbedWidget
7 |
8 |
9 | class UserInteractionMainWindow(object):
10 |
11 | def setupUi(self, main_window):
12 |
13 | main_window.setObjectName('MainWindow')
14 | main_window.resize(1300, 900)
15 | main_window.setWindowTitle('SimpleFOC Configuration Tool ')
16 |
17 | self.centralwidget = QtWidgets.QWidget(main_window)
18 | self.centralwidget.setObjectName('centralwidget')
19 |
20 | # Add layout de to the main window
21 | self.horizontalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
22 | self.horizontalLayout.setObjectName('verticalLayout')
23 |
24 | # Add tabebd tools widget to the main window
25 | self.tabbedToolsWidget = WorkAreaTabbedWidget(self.centralwidget)
26 | self.horizontalLayout.addWidget(self.tabbedToolsWidget)
27 |
28 | # Add toolbar to the main window
29 | self.toolBar = SimpleFOCConfigToolBar(main_window,self.tabbedToolsWidget, main_window)
30 | main_window.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
31 |
32 | # Add status bar to the main window
33 | self.statusbar = QtWidgets.QStatusBar(main_window)
34 | self.statusbar.setObjectName('statusbar')
35 | main_window.setStatusBar(self.statusbar)
36 |
37 | # Add central Widget to the main window
38 | main_window.setCentralWidget(self.centralwidget)
39 |
40 |
--------------------------------------------------------------------------------
/src/gui/resources/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/add.png
--------------------------------------------------------------------------------
/src/gui/resources/add_motor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/add_motor.png
--------------------------------------------------------------------------------
/src/gui/resources/add_motor.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/add_motor.psd
--------------------------------------------------------------------------------
/src/gui/resources/alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/alert.png
--------------------------------------------------------------------------------
/src/gui/resources/ard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/ard.png
--------------------------------------------------------------------------------
/src/gui/resources/backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/backward.png
--------------------------------------------------------------------------------
/src/gui/resources/bluedot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/bluedot.png
--------------------------------------------------------------------------------
/src/gui/resources/configure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/configure.png
--------------------------------------------------------------------------------
/src/gui/resources/connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/connect.png
--------------------------------------------------------------------------------
/src/gui/resources/consoletool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/consoletool.png
--------------------------------------------------------------------------------
/src/gui/resources/continue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/continue.png
--------------------------------------------------------------------------------
/src/gui/resources/customcommands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/customcommands.png
--------------------------------------------------------------------------------
/src/gui/resources/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/delete.png
--------------------------------------------------------------------------------
/src/gui/resources/disconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/disconnect.png
--------------------------------------------------------------------------------
/src/gui/resources/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/edit.png
--------------------------------------------------------------------------------
/src/gui/resources/fastbackward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/fastbackward.png
--------------------------------------------------------------------------------
/src/gui/resources/fastfordward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/fastfordward.png
--------------------------------------------------------------------------------
/src/gui/resources/fordward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/fordward.png
--------------------------------------------------------------------------------
/src/gui/resources/form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/form.png
--------------------------------------------------------------------------------
/src/gui/resources/gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/gear.png
--------------------------------------------------------------------------------
/src/gui/resources/gen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/gen.png
--------------------------------------------------------------------------------
/src/gui/resources/generalsettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/generalsettings.png
--------------------------------------------------------------------------------
/src/gui/resources/greendot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/greendot.png
--------------------------------------------------------------------------------
/src/gui/resources/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/home.png
--------------------------------------------------------------------------------
/src/gui/resources/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/list.png
--------------------------------------------------------------------------------
/src/gui/resources/loop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/loop.png
--------------------------------------------------------------------------------
/src/gui/resources/maroondot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/maroondot.png
--------------------------------------------------------------------------------
/src/gui/resources/motor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/motor.png
--------------------------------------------------------------------------------
/src/gui/resources/motor.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/motor.psd
--------------------------------------------------------------------------------
/src/gui/resources/motor1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/motor1.png
--------------------------------------------------------------------------------
/src/gui/resources/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/open.png
--------------------------------------------------------------------------------
/src/gui/resources/orangedot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/orangedot.png
--------------------------------------------------------------------------------
/src/gui/resources/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/pause.png
--------------------------------------------------------------------------------
/src/gui/resources/pid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/pid.png
--------------------------------------------------------------------------------
/src/gui/resources/pidconfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/pidconfig.png
--------------------------------------------------------------------------------
/src/gui/resources/pull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/pull.png
--------------------------------------------------------------------------------
/src/gui/resources/purpledot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/purpledot.png
--------------------------------------------------------------------------------
/src/gui/resources/push.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/push.png
--------------------------------------------------------------------------------
/src/gui/resources/reddot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/reddot.png
--------------------------------------------------------------------------------
/src/gui/resources/res.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/res.png
--------------------------------------------------------------------------------
/src/gui/resources/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/save.png
--------------------------------------------------------------------------------
/src/gui/resources/send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/send.png
--------------------------------------------------------------------------------
/src/gui/resources/sensor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/sensor.png
--------------------------------------------------------------------------------
/src/gui/resources/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/start.png
--------------------------------------------------------------------------------
/src/gui/resources/statistics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/statistics.png
--------------------------------------------------------------------------------
/src/gui/resources/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/stop.png
--------------------------------------------------------------------------------
/src/gui/resources/stopjogging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/stopjogging.png
--------------------------------------------------------------------------------
/src/gui/resources/studioicon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/studioicon.icns
--------------------------------------------------------------------------------
/src/gui/resources/tree copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/tree copy.png
--------------------------------------------------------------------------------
/src/gui/resources/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/tree.png
--------------------------------------------------------------------------------
/src/gui/resources/yellowdot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/yellowdot.png
--------------------------------------------------------------------------------
/src/gui/resources/zoomall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Deego-Robotics/SimpleFOCStudio-zh/e8a67ee5534c8d1e948559af155009c9401a0e31/src/gui/resources/zoomall.png
--------------------------------------------------------------------------------
/src/gui/sharedcomnponets/commandLineInterface.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtGui, QtWidgets
4 |
5 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
6 | from src.simpleFOCConnector import SimpleFOCDevice
7 |
8 |
9 | class CommandLineWidget(QtWidgets.QGroupBox):
10 |
11 | def __init__(self, parent=None):
12 | super().__init__(parent)
13 |
14 | self.device = SimpleFOCDevice.getInstance()
15 |
16 | self.setObjectName('groupBox')
17 | self.setTitle('命令行交互界面')
18 |
19 | self.cmlVerticalLayout = QtWidgets.QVBoxLayout(self)
20 | self.cmlVerticalLayout.setObjectName('cmlVerticalLayout')
21 |
22 | self.commandLineDisplay = QtWidgets.QTextEdit(self)
23 | self.commandLineDisplay.setObjectName('commandLineDisplay')
24 | self.cmlVerticalLayout.addWidget(self.commandLineDisplay)
25 |
26 | self.commandLineDisplay.setReadOnly(True)
27 | self.commandLineDisplay.setTextColor(QtGui.QColor(68, 117, 68, 255))
28 |
29 | self.cmlWidget = QtWidgets.QWidget(self)
30 | self.cmlWidget.setObjectName('cmlWidget')
31 | self.cmlHorizontalLayout = QtWidgets.QHBoxLayout(self.cmlWidget)
32 | self.cmlHorizontalLayout.setObjectName('cmlHorizontalLayout')
33 |
34 | self.commandLineEdit = QtWidgets.QLineEdit(self.cmlWidget)
35 | self.commandLineEdit.setObjectName('commandLineEdit')
36 | self.cmlHorizontalLayout.addWidget(self.commandLineEdit)
37 |
38 | self.commandLineEdit.returnPressed.connect(self.sendAction)
39 |
40 | self.sendButton = QtWidgets.QPushButton(self.cmlWidget)
41 | self.sendButton.setObjectName('sendButton')
42 | self.sendButton.setText('发送')
43 | self.sendButton.setIcon(GUIToolKit.getIconByName('send'))
44 | self.sendButton.clicked.connect(self.sendAction)
45 |
46 | self.cmlHorizontalLayout.addWidget(self.sendButton)
47 |
48 | self.clearButton = QtWidgets.QPushButton(self.cmlWidget)
49 | self.clearButton.setObjectName('clearButton')
50 | self.cmlHorizontalLayout.addWidget(self.clearButton)
51 | self.clearButton.setIcon(GUIToolKit.getIconByName('delete'))
52 | self.clearButton.clicked.connect(self.clearAction)
53 | self.clearButton.setText('清除')
54 |
55 | self.listDevices = QtWidgets.QPushButton(self.cmlWidget)
56 | self.listDevices.setObjectName('listDevices')
57 | self.cmlHorizontalLayout.addWidget(self.listDevices)
58 | self.listDevices.setIcon(GUIToolKit.getIconByName('list'))
59 | self.listDevices.clicked.connect(self.sendListDevices)
60 | self.listDevices.setText('设备列表')
61 |
62 | self.cmlVerticalLayout.addWidget(self.cmlWidget)
63 | self.device.addConnectionStateListener(self)
64 | self.setEnabled(False)
65 |
66 | def connectionStateChanged(self, deviceConnected):
67 | if deviceConnected is True:
68 | self.enabeUI()
69 | self.publishCommandResponseData('已连接 ...')
70 | else:
71 | self.disableUI()
72 | self.publishCommandResponseData('已断开 ...')
73 |
74 | def enabeUI(self):
75 |
76 | self.commandLineDisplay.setEnabled(True)
77 | self.cmlWidget.setEnabled(True)
78 | self.commandLineEdit.setEnabled(True)
79 | self.sendButton.setEnabled(True)
80 |
81 | self.setEnabled(True)
82 |
83 | def disableUI(self):
84 | # self.commandLineDisplay.setEnabled(False)
85 | self.commandLineEdit.setEnabled(False)
86 | self.sendButton.setEnabled(False)
87 |
88 | def publishCommandResponseData(self, data):
89 | self.commandLineDisplay.append(data)
90 | self.commandLineDisplay.moveCursor(QtGui.QTextCursor.End)
91 |
92 | def clearAction(self):
93 | self.commandLineDisplay.setPlainText('')
94 |
95 | def sendAction(self):
96 | self.device.sendCommand(self.commandLineEdit.text())
97 | self.commandLineEdit.setText('')
98 |
99 | def sendListDevices(self):
100 | self.device.sendCommand('?')
101 |
--------------------------------------------------------------------------------
/src/gui/sharedcomnponets/sharedcomponets.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import os
4 |
5 | from PyQt5 import QtGui, QtWidgets, QtCore
6 | from serial.tools import list_ports
7 |
8 |
9 | class GUIToolKit(object):
10 | ''' This class is used to provide icons for the rest of the application
11 | hiding the location of the resources
12 | '''
13 | RED_COLOR = (255, 92, 92)
14 | GREEN_COLOR = (57, 217, 138)
15 | BLUE_COLOR = (91, 141, 236)
16 | ORANGE_COLOR = (253, 172, 66)
17 | YELLOW_COLOR = (255,255,51)
18 | PURPLE_COLOR = (75,0,130)
19 | MAROON_COLOR = (222,184,135)
20 |
21 | @staticmethod
22 | def getIconByName(icoName):
23 |
24 | file_index = {
25 | 'add': 'add.png',
26 | 'add_motor': 'add_motor.png',
27 | 'tree': 'tree.png',
28 | 'gen': 'gen.png',
29 | 'home': 'home.png',
30 | 'form': 'form.png',
31 | 'edit': 'edit.png',
32 | 'delete': 'delete.png',
33 | 'statistics': 'statistics.png',
34 | 'reddot': 'reddot.png',
35 | 'orangedot': 'orangedot.png',
36 | 'greendot': 'greendot.png',
37 | 'bluedot': 'bluedot.png',
38 | 'purpledot': 'purpledot.png',
39 | 'yellowdot': 'yellowdot.png',
40 | 'maroondot': 'maroondot.png',
41 | 'send': 'send.png',
42 | 'zoomall': 'zoomall.png',
43 | 'connect': 'connect.png',
44 | 'continue': 'continue.png',
45 | 'alert': 'alert.png',
46 | 'gear': 'gear.png',
47 | 'generalsettings': 'generalsettings.png',
48 | 'open': 'open.png',
49 | 'loop': 'loop.png',
50 | 'save': 'save.png',
51 | 'stop': 'stop.png',
52 | 'restart': 'continue.png',
53 | 'res': 'res.png',
54 | 'sensor': 'sensor.png',
55 | 'start': 'start.png',
56 | 'motor': 'motor.png',
57 | 'pause': 'pause.png',
58 | 'pull': 'pull.png',
59 | 'push': 'push.png',
60 | 'list': 'list.png',
61 | 'disconnect': 'disconnect.png',
62 | 'configure': 'configure.png',
63 | 'pidconfig': 'pidconfig.png',
64 | 'consoletool': 'consoletool.png',
65 | 'fordward': 'fordward.png',
66 | 'fastbackward': 'fastbackward.png',
67 | 'backward': 'backward.png',
68 | 'stopjogging': 'stopjogging.png',
69 | 'fastfordward': 'fastfordward.png',
70 | 'customcommands':'customcommands.png'
71 | }
72 | currentDir = os.path.dirname(__file__)
73 | icon_path = os.path.join(currentDir, '../resources', file_index[icoName])
74 | icon = QtGui.QIcon()
75 | icon.addPixmap(QtGui.QPixmap(icon_path), QtGui.QIcon.Normal,
76 | QtGui.QIcon.Off)
77 | return icon
78 |
79 |
80 | class ConfigQLineEdit(QtWidgets.QLineEdit):
81 | return_key = 16777220
82 | updateValue = QtCore.pyqtSignal()
83 | def __init__(self, parent=None):
84 | '''Constructor for ToolsWidget'''
85 | super().__init__(parent)
86 |
87 | def keyPressEvent(self, event):
88 | if event.key() == self.return_key:
89 | self.updateValue.emit()
90 | else:
91 | super().keyPressEvent(event)
92 |
93 | class WorkAreaTabWidget(QtWidgets.QTabWidget):
94 | def __init__(self, parent=None):
95 | '''Constructor for ToolsWidget'''
96 | super().__init__(parent)
97 |
98 | def getTabIcon(self):
99 | raise NotImplemented
100 |
101 | def getTabName(self):
102 | raise NotImplemented
103 |
104 | class SerialPortComboBox(QtWidgets.QComboBox):
105 | def __init__(self, parent=None, snifer=None):
106 | super().__init__(parent)
107 | self.addItems(self.getAvailableSerialPortNames())
108 |
109 | def getAvailableSerialPortNames(self):
110 | portNames = []
111 | for port in list_ports.comports():
112 | if port[2] != 'n/a':
113 | portNames.append(port[0])
114 |
115 | return portNames
116 |
117 | def showPopup(self):
118 | selectedItem = self.currentText()
119 | super().clear()
120 | availableSerialPortNames = self.getAvailableSerialPortNames()
121 | self.addItems(availableSerialPortNames)
122 | if selectedItem in availableSerialPortNames:
123 | self.setCurrentText(selectedItem)
124 | super().showPopup()
125 |
--------------------------------------------------------------------------------
/src/gui/toolbar.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from PyQt5 import QtWidgets
4 | from src.gui.sharedcomnponets.sharedcomponets import GUIToolKit
5 |
6 |
7 | class SimpleFOCConfigToolBar(QtWidgets.QToolBar):
8 |
9 | def __init__(self,main_window, devicesTabedWidget, parent=None):
10 | super().__init__(parent)
11 |
12 | self.addDeviceAction = QtWidgets.QToolButton(main_window)
13 | self.addDeviceAction.setIcon(GUIToolKit.getIconByName('add_motor'))
14 | self.addDeviceAction.setObjectName('addDeviceAction')
15 | self.addDeviceAction.setPopupMode(QtWidgets.QToolButton.InstantPopup)
16 |
17 | self.addDeviceMenu = QtWidgets.QMenu(self.addDeviceAction)
18 | self.addDeviceTreeView = QtWidgets.QAction("树形视图",self.addDeviceMenu)
19 | self.addDeviceTreeView.setIcon(GUIToolKit.getIconByName('tree'))
20 | self.addDeviceTreeView.triggered.connect(devicesTabedWidget.addDeviceTree)
21 | self.addDeviceFormView = QtWidgets.QAction("表格视图",self.addDeviceMenu)
22 | self.addDeviceFormView.setIcon(GUIToolKit.getIconByName('form'))
23 | self.addDeviceFormView.triggered.connect(devicesTabedWidget.addDeviceForm)
24 |
25 | self.addDeviceMenu.addAction(self.addDeviceTreeView)
26 | self.addDeviceMenu.addAction(self.addDeviceFormView)
27 | self.addDeviceAction.setMenu(self.addDeviceMenu)
28 | self.addWidget(self.addDeviceAction)
29 |
30 | self.openDeviceAction = QtWidgets.QAction(main_window)
31 | self.openDeviceAction.setIcon(GUIToolKit.getIconByName('open'))
32 | self.openDeviceAction.setObjectName('openDeviceAction')
33 | self.openDeviceAction.triggered.connect(devicesTabedWidget.openDevice)
34 |
35 | self.addAction(self.openDeviceAction)
36 |
37 | self.saveDeviceAction = QtWidgets.QAction(main_window)
38 | self.saveDeviceAction.setIcon(GUIToolKit.getIconByName('save'))
39 | self.saveDeviceAction.setObjectName('saveDeviceAction')
40 | self.saveDeviceAction.triggered.connect(devicesTabedWidget.saveDevice)
41 |
42 | self.addAction(self.saveDeviceAction)
43 |
44 | self.generateCodeAction = QtWidgets.QAction(main_window)
45 | self.generateCodeAction.setIcon(GUIToolKit.getIconByName('gen'))
46 | self.generateCodeAction.setObjectName('genertecode')
47 | self.generateCodeAction.triggered.connect(devicesTabedWidget.generateCode)
48 |
49 | self.addAction(self.generateCodeAction)
50 | self.addSeparator()
51 |
52 | self.openConsoleToolAction = QtWidgets.QAction(main_window)
53 | self.openConsoleToolAction.setIcon(GUIToolKit.getIconByName('consoletool'))
54 | self.openConsoleToolAction.setToolTip('Open Serial Cosole tool')
55 | self.openConsoleToolAction.setObjectName('openconsoletool')
56 | self.openConsoleToolAction.triggered.connect(devicesTabedWidget.openConsoleTool)
57 |
58 | self.addAction(self.openConsoleToolAction)
59 |
60 | self.addSeparator()
61 |
--------------------------------------------------------------------------------
/src/gui/workAreaTabbedWidget.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import json
4 |
5 | from PyQt5 import QtWidgets
6 |
7 | from src.gui.commandlinetool.commandlinetool import CommandLineConsoleTool
8 | from src.gui.configtool.deviceConfigurationTool import DeviceConfigurationTool
9 | from src.gui.configtool.generatedCodeDisplay import GeneratedCodeDisplay
10 | from src.gui.configtool.treeViewConfigTool import TreeViewConfigTool
11 | from src.simpleFOCConnector import SimpleFOCDevice
12 |
13 |
14 | class WorkAreaTabbedWidget(QtWidgets.QTabWidget):
15 |
16 | def __init__(self, parent=None):
17 | super().__init__(parent)
18 | self.setTabsClosable(True)
19 | self.setMovable(True)
20 | self.setObjectName('devicesTabWidget')
21 |
22 | self.device = SimpleFOCDevice.getInstance()
23 |
24 | self.cmdLineTool = None
25 | self.configDeviceTool = None
26 | self.generatedCodeTab = None
27 | self.activeToolsList = []
28 |
29 | self.tabCloseRequested.connect(self.removeTabHandler)
30 |
31 | self.setStyleSheet(
32 | 'QTabBar::close - button { image: url(close.png) subcontrol - position: left; }')
33 | self.setStyleSheet('QTabBar::tab { height: 30px; width: 150px;}')
34 |
35 | def removeTabHandler(self, index):
36 | if type(self.currentWidget()) == CommandLineConsoleTool:
37 | self.cmdLineTool = None
38 | if type(self.currentWidget()) == DeviceConfigurationTool or type(
39 | self.currentWidget()) == TreeViewConfigTool:
40 | self.configDeviceTool = None
41 | if type(self.currentWidget()) == GeneratedCodeDisplay:
42 | self.generatedCodeTab = None
43 | if self.configDeviceTool == None and self.cmdLineTool == None:
44 | if self.device.isConnected:
45 | self.device.disConnect()
46 |
47 | self.activeToolsList.pop(index)
48 | self.removeTab(index)
49 |
50 | def addDeviceForm(self):
51 | if self.configDeviceTool is None:
52 | self.configDeviceTool = DeviceConfigurationTool()
53 | self.activeToolsList.append(self.configDeviceTool)
54 | self.addTab(self.configDeviceTool,
55 | self.configDeviceTool.getTabIcon(), 'Device')
56 | self.setCurrentIndex(self.currentIndex() + 1)
57 |
58 | def addDeviceTree(self):
59 | if self.configDeviceTool is None:
60 | self.configDeviceTool = TreeViewConfigTool()
61 | self.activeToolsList.append(self.configDeviceTool)
62 | self.addTab(self.configDeviceTool,
63 | self.configDeviceTool.getTabIcon(), 'Device')
64 | self.setCurrentIndex(self.currentIndex() + 1)
65 |
66 | def openDevice(self):
67 | if self.configDeviceTool is None:
68 | dlg = QtWidgets.QFileDialog()
69 | dlg.setFileMode(QtWidgets.QFileDialog.AnyFile)
70 | filenames = None
71 | if dlg.exec_():
72 | filenames = dlg.selectedFiles()
73 | try:
74 | with open(filenames[0]) as json_file:
75 | configurationInfo = json.load(json_file)
76 | sfd = SimpleFOCDevice.getInstance()
77 | sfd.configureDevice(configurationInfo)
78 | self.configDeviceTool = TreeViewConfigTool()
79 | sfd.openedFile = filenames
80 | self.activeToolsList.append(self.configDeviceTool)
81 | tabName = self.configDeviceTool.getTabName()
82 | if tabName == '':
83 | tabName = 'Device'
84 | self.addTab(self.configDeviceTool,
85 | self.configDeviceTool.getTabIcon(), tabName)
86 | self.setCurrentIndex(self.currentIndex() + 1)
87 |
88 | except Exception as exception:
89 | msgBox = QtWidgets.QMessageBox()
90 | msgBox.setIcon(QtWidgets.QMessageBox.Warning)
91 | msgBox.setText('打开文件时出错')
92 | msgBox.setWindowTitle('SimpleFOC configDeviceTool')
93 | msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
94 | msgBox.exec()
95 |
96 | def saveDevice(self):
97 | if len(self.activeToolsList) > 0:
98 | currentconfigDeviceTool = self.activeToolsList[self.currentIndex()]
99 | if currentconfigDeviceTool.device.openedFile is None:
100 | options = QtWidgets.QFileDialog.Options()
101 | options |= QtWidgets.QFileDialog.DontUseNativeDialog
102 | fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self,
103 | '保存电机配置参数',
104 | '',
105 | 'JSON configuration file (*.json)',
106 | options=options)
107 | if fileName:
108 | self.saveToFile(currentconfigDeviceTool.device, fileName)
109 | else:
110 | self.saveToFile(currentconfigDeviceTool.device,
111 | currentconfigDeviceTool.device.openedFile)
112 |
113 | def generateCode(self):
114 | if len(self.activeToolsList) > 0:
115 | currentconfigDeviceTool = self.activeToolsList[self.currentIndex()]
116 | self.generatedCodeTab = GeneratedCodeDisplay()
117 | self.activeToolsList.append(self.generatedCodeTab)
118 | self.addTab(self.generatedCodeTab,
119 | self.generatedCodeTab.getTabIcon(), self.generatedCodeTab.getTabName())
120 | self.setCurrentIndex(self.currentIndex() + 1)
121 |
122 |
123 | def saveToFile(self, deviceToSave, file):
124 | if type(file) is list:
125 | with open(file[0], 'w', encoding='utf-8') as f:
126 | f.write(json.dumps(deviceToSave.toJSON(), indent=4, sort_keys=True))
127 | else:
128 | with open(file, 'w', encoding='utf-8') as f:
129 | f.write(json.dumps(deviceToSave.toJSON(), indent=4, sort_keys=True))
130 |
131 | def openConsoleTool(self):
132 | if self.cmdLineTool is None:
133 | self.cmdLineTool = CommandLineConsoleTool()
134 | self.activeToolsList.append(self.cmdLineTool)
135 | self.addTab(self.cmdLineTool,
136 | self.cmdLineTool.getTabIcon(), '命令行交互')
137 | self.setCurrentIndex(self.currentIndex() + 1)
138 |
--------------------------------------------------------------------------------
/src/simpleFOCConnector.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import logging
4 | import threading
5 | import time
6 |
7 | import serial
8 | from PyQt5 import QtCore, QtWidgets
9 | from serial import SerialException
10 | from collections import defaultdict
11 |
12 | class PIDController:
13 | P = 0
14 | D = 0
15 | I = 0
16 | outputRamp = 0
17 | outputLimit = 0
18 | cmd =''
19 |
20 | def __init__(self, cmd):
21 | self.cmd = cmd
22 |
23 | def load(self, jsonValues):
24 | self.P = jsonValues['P']
25 | self.I = jsonValues['I']
26 | self.D = jsonValues['D']
27 | self.outputRamp = jsonValues['outputRamp']
28 | self.outputLimit = jsonValues['outputLimit']
29 |
30 | def serialize(self):
31 | return {
32 | 'P': self.P,
33 | 'I': self.I,
34 | 'D': self.D,
35 | 'outputRamp':self.outputRamp,
36 | 'outputLimit':self.outputLimit
37 | }
38 |
39 |
40 | class LowPassFilter:
41 | Tf = 0
42 | cmd =''
43 | cmdTf = 'F'
44 |
45 | def __init__(self, cmd):
46 | self.cmd = cmd
47 |
48 |
49 | class Command:
50 | cmdName = ''
51 | cmd = ''
52 |
53 | def __init__(self,cmdname='Command', cmd=''):
54 | self.cmdName = cmdname
55 | self.cmd = cmd
56 |
57 | def load(self, jsonValues):
58 | self.cmdName = jsonValues['commandName']
59 | self.cmd = jsonValues['commandValue']
60 |
61 | def serialize(self):
62 | return {
63 | 'commandName': self.cmdName,
64 | 'commandValue': self.cmd
65 | }
66 |
67 |
68 | class CustomCommands:
69 | customCommandsList = []
70 |
71 | def __init__(self,commandsLis=[]):
72 | for command in commandsLis:
73 | self.customCommandsList.append(command)
74 | def load(self, jsonValues):
75 | for commandInJson in jsonValues:
76 | command = Command()
77 | command.load(commandInJson)
78 | self.customCommandsList.append(command)
79 |
80 | def serialize(self):
81 | serializedCustomCommands = defaultdict(list)
82 | for command in self.customCommandsList:
83 | serializedCommand = command.serialize()
84 | serializedCustomCommands['customCommands'].append(serializedCommand)
85 | return serializedCustomCommands
86 |
87 | class SimpleFOCDevice:
88 | __instance = None
89 |
90 | TORQUE_CONTROL = 0
91 | VELOCITY_CONTROL = 1
92 | ANGLE_CONTROL = 2
93 | VELOCITY_OPENLOOP_CONTROL = 3
94 | ANGLE_OPENLOOP_CONTROL = 4
95 |
96 | SINE_PWM = 0
97 | SPACE_VECTOR_PWM = 1
98 | TRAPEZOIDAL_120 = 2
99 | TRAPEZOIDAL_150 = 3
100 |
101 | VOLTAGE_TORQUE = 0
102 | DC_CURRENT_TORQUE = 1
103 | FOC_CURRENT_TORQUE = 2
104 |
105 | VELOCITY_PID = 'V'
106 | ANGLE_PID = 'A'
107 | CURRENT_Q_PID = 'Q'
108 | CURRENT_D_PID = 'D'
109 |
110 | PULL_CONFIG_ON_CONNECT = 'Pull config'
111 | PUSH_CONFG_ON_CONNECT = 'Push config'
112 | ONLY_CONNECT = 'Only connect'
113 |
114 | @staticmethod
115 | def getInstance():
116 | """ Static access method. """
117 | if SimpleFOCDevice.__instance == None:
118 | SimpleFOCDevice()
119 | return SimpleFOCDevice.__instance
120 |
121 | def __init__(self):
122 | """ Virtually private constructor. """
123 | if SimpleFOCDevice.__instance != None:
124 | raise Exception("This class is a singleton!")
125 | else:
126 | # serial connection variables
127 | self.serialPort = None
128 | self.responseThread = None
129 | self.isConnected = False
130 | self.openedFile = None
131 |
132 | self.connectionStateListenerList = []
133 |
134 | self.serialPortName = ""
135 | self.serialRate = 115200
136 | self.serialByteSize = serial.EIGHTBITS
137 | self.serialParity = serial.PARITY_NONE
138 | self.stopBits = serial.STOPBITS_ONE
139 | self.commProvider = SerialPortReceiveHandler()
140 | self.commProvider.commandDataReceived.connect(self.parseResponses)
141 | self.commProvider.stateMonitorReceived.connect(self.parseStateResponses)
142 | self.connectionID = ""
143 |
144 | # command id of the device
145 | self.devCommandID = ''
146 |
147 | # motion control paramters
148 | self.PIDVelocity = PIDController(self.VELOCITY_PID)
149 | self.PIDAngle = PIDController(self.ANGLE_PID)
150 | self.PIDCurrentQ = PIDController(self.CURRENT_Q_PID)
151 | self.PIDCurrentD = PIDController(self.CURRENT_D_PID)
152 | self.LPFVelocity = LowPassFilter(self.VELOCITY_PID)
153 | self.LPFAngle = LowPassFilter(self.ANGLE_PID)
154 | self.LPFCurrentQ = LowPassFilter(self.CURRENT_Q_PID)
155 | self.LPFCurrentD = LowPassFilter(self.CURRENT_D_PID)
156 | self.velocityLimit = 0
157 | self.voltageLimit = 0
158 | self.currentLimit = 0
159 | self.controlType = SimpleFOCDevice.ANGLE_CONTROL
160 | self.torqueType = SimpleFOCDevice.VOLTAGE_TORQUE
161 | self.initialTarget = 0
162 | self.motionDownsample = 0
163 |
164 | # monitor variables
165 | self.monitorDownsample = 0
166 | self.monitorVariables = 0
167 |
168 | # state variables
169 | self.target = 0
170 | self.stateUpdater = StateUpdateRunner(self)
171 | self.targetNow = 0
172 | self.angleNow = 0
173 | self.velocityNow = 0
174 | self.voltageQNow = 0
175 | self.voltageDNow = 0
176 | self.currentQNow = 0
177 | self.currentDNow = 0
178 |
179 | # general variables
180 | self.phaseResistance = 0
181 | self.deviceStatus = 0
182 | self.modulationType = 0
183 | self.modulationCentered = 1
184 |
185 | # sensor variables
186 | self.sensorElectricalZero = 0
187 | self.sensorZeroOffset = 0
188 |
189 | # list of custom commands
190 | self.customCommands = CustomCommands()
191 |
192 | # return the class instance
193 | SimpleFOCDevice.__instance = self
194 |
195 |
196 | def configureDevice(self, jsonValue):
197 | # motion control parameters
198 | self.PIDVelocity.load(jsonValue['PIDVelocity'])
199 | self.PIDAngle.load(jsonValue['PIDAngle'])
200 | self.PIDCurrentD.load(jsonValue['PIDCurrentD'])
201 | self.PIDCurrentQ.load(jsonValue['PIDCurrentQ'])
202 |
203 | # low pass filters
204 | self.LPFVelocity.Tf = jsonValue['LPFVelocity']
205 | self.LPFAngle.Tf = jsonValue['LPFAngle']
206 | self.LPFCurrentQ.Tf = jsonValue['LPFCurrentQ']
207 | self.LPFCurrentD.Tf = jsonValue['LPFCurrentD']
208 | # limit variables
209 | self.velocityLimit = jsonValue['velocityLimit']
210 | self.voltageLimit = jsonValue['voltageLimit']
211 | self.currentLimit = jsonValue['currentLimit']
212 | # motion control types
213 | self.controlType = jsonValue['controlType']
214 | self.torqueType = jsonValue['torqueType']
215 | self.motionDownsample = jsonValue['motionDownsample']
216 |
217 | # sensor zero offset and electrical zero offset
218 | self.sensorElectricalZero = jsonValue['sensorElectricalZero']
219 | self.sensorZeroOffset = jsonValue['sensorZeroOffset']
220 |
221 | # motor phase resistance
222 | self.phaseResistance = jsonValue['phaseResistance']
223 |
224 | # initial target
225 | self.initialTarget = jsonValue['initialTarget']
226 |
227 | # serial communication variables
228 | self.connectionID = jsonValue['connectionID']
229 | self.serialPortName = jsonValue['serialPortName']
230 | self.serialRate = jsonValue['serialRate']
231 | self.serialByteSize = jsonValue['serialByteSize']
232 | self.serialParity = jsonValue['serialParity']
233 | self.stopBits = jsonValue['stopBits']
234 | try:
235 | self.customCommands.customCommandsList = []
236 | self.customCommands.load(jsonValue['customCommands'])
237 | except KeyError:
238 | pass
239 | try:
240 | self.devCommandID = jsonValue['devCommandID']
241 | except KeyError:
242 | pass
243 |
244 | def configureConnection(self, configDict):
245 | self.connectionID = configDict['connectionID']
246 | self.serialPortName = configDict['serialPortName']
247 | self.serialRate = configDict['serialRate']
248 | self.serialByteSize = configDict['serialByteSize']
249 | self.serialParity = configDict['serialParity']
250 | self.stopBits = configDict['stopBits']
251 |
252 | def toJSON(self):
253 | valuesToSave = {
254 | 'PIDVelocity': self.PIDVelocity.serialize(),
255 | 'PIDAngle': self.PIDAngle.serialize(),
256 | 'PIDCurrentD': self.PIDCurrentD.serialize(),
257 | 'PIDCurrentQ': self.PIDCurrentQ.serialize(),
258 | 'LPFVelocity':self.LPFVelocity.Tf,
259 | 'LPFAngle':self.LPFAngle.Tf,
260 | 'LPFCurrentD':self.LPFCurrentD.Tf,
261 | 'LPFCurrentQ':self.LPFCurrentQ.Tf,
262 | 'velocityLimit': self.velocityLimit,
263 | 'voltageLimit': self.voltageLimit,
264 | 'currentLimit': self.currentLimit,
265 | 'controlType': self.controlType,
266 | 'motionDownsample':self.motionDownsample,
267 | 'torqueType': self.torqueType,
268 | 'phaseResistance': self.phaseResistance,
269 | 'sensorZeroOffset': self.sensorZeroOffset,
270 | 'sensorElectricalZero': self.sensorElectricalZero,
271 | 'initialTarget': self.initialTarget,
272 | 'connectionID': self.connectionID,
273 | 'serialPortName': self.serialPortName,
274 | 'serialRate': self.serialRate,
275 | 'serialByteSize': self.serialByteSize,
276 | 'serialParity': self.serialParity,
277 | 'stopBits': self.stopBits,
278 | 'devCommandID': self.devCommandID,
279 |
280 | }
281 | valuesToSave.update(self.customCommands.serialize())
282 | return valuesToSave
283 |
284 |
285 | def toArduinoCode(self, generateParams = []):
286 |
287 | # code = "#include \n\n"
288 | # code += "void setup(){\n\n"
289 | # code += "....\n\n"
290 | code = "\n"
291 | if generateParams[0] or generateParams==[]:
292 | code += "// control loop type and torque mode \n"
293 | code += "motor.torque_controller = TorqueControlType::"
294 | if self.torqueType == self.VOLTAGE_TORQUE:
295 | code += "voltage"
296 | elif self.torqueType == self.DC_CURRENT_TORQUE:
297 | code += "dc_current"
298 | elif self.torqueType == self.FOC_CURRENT_TORQUE:
299 | code += "foc_current"
300 | code += ";\n"
301 | code += "motor.controller = MotionControlType::"
302 | if self.controlType == self.TORQUE_CONTROL:
303 | code += "torque"
304 | elif self.controlType == self.VELOCITY_CONTROL:
305 | code += "velocity"
306 | elif self.controlType == self.ANGLE_CONTROL:
307 | code += "angle"
308 | elif self.controlType == self.VELOCITY_CONTROL:
309 | code += "velocity_openloop"
310 | elif self.controlType == self.ANGLE_CONTROL:
311 | code += "angle_openloop"
312 | code += ";\n"
313 | code += "motor.motion_downsample = " + str(self.motionDownsample) +";\n"
314 | code += "\n"
315 |
316 | if generateParams[1] or generateParams==[]:
317 | code += "// velocity loop PID\n"
318 | code += "motor.PID_velocity.P = " + str(self.PIDVelocity.P) +";\n"
319 | code += "motor.PID_velocity.I = " + str(self.PIDVelocity.I) +";\n"
320 | code += "motor.PID_velocity.D = " + str(self.PIDVelocity.D) +";\n"
321 | code += "motor.PID_velocity.output_ramp = " + str(self.PIDVelocity.outputRamp) +";\n"
322 | code += "motor.PID_velocity.limit = " + str(self.PIDVelocity.outputLimit) +";\n"
323 | code += "// Low pass filtering time constant \n"
324 | code += "motor.LPF_velocity.Tf = " + str(self.LPFVelocity.Tf) +";\n"
325 | if generateParams[2] or generateParams==[]:
326 | code += "// angle loop PID\n"
327 | code += "motor.P_angle.P = " + str(self.PIDAngle.P) +";\n"
328 | code += "motor.P_angle.I = " + str(self.PIDAngle.I) +";\n"
329 | code += "motor.P_angle.D = " + str(self.PIDAngle.D) +";\n"
330 | code += "motor.P_angle.output_ramp = " + str(self.PIDAngle.outputRamp) +";\n"
331 | code += "motor.P_angle.limit = " + str(self.PIDAngle.outputLimit) +";\n"
332 | code += "// Low pass filtering time constant \n"
333 | code += "motor.LPF_angle.Tf = " + str(self.LPFAngle.Tf) +";\n"
334 | if generateParams[3] or generateParams==[]:
335 | code += "// current q loop PID \n"
336 | code += "motor.PID_current_q.P = " + str(self.PIDCurrentQ.P) +";\n"
337 | code += "motor.PID_current_q.I = " + str(self.PIDCurrentQ.I) +";\n"
338 | code += "motor.PID_current_q.D = " + str(self.PIDCurrentQ.D) +";\n"
339 | code += "motor.PID_current_q.output_ramp = " + str(self.PIDCurrentQ.outputRamp) +";\n"
340 | code += "motor.PID_current_q.limit = " + str(self.PIDCurrentQ.outputLimit) +";\n"
341 | code += "// Low pass filtering time constant \n"
342 | code += "motor.LPF_current_q.Tf = " + str(self.LPFCurrentQ.Tf) +";\n"
343 | if generateParams[4] or generateParams==[]:
344 | code += "// current d loop PID\n"
345 | code += "motor.PID_current_d.P = " + str(self.PIDCurrentD.P) +";\n"
346 | code += "motor.PID_current_d.I = " + str(self.PIDCurrentD.I) +";\n"
347 | code += "motor.PID_current_d.D = " + str(self.PIDCurrentD.D) +";\n"
348 | code += "motor.PID_current_d.output_ramp = " + str(self.PIDCurrentD.outputRamp) +";\n"
349 | code += "motor.PID_current_d.limit = " + str(self.PIDCurrentD.outputLimit) +";\n"
350 | code += "// Low pass filtering time constant \n"
351 | code += "motor.LPF_current_d.Tf = " + str(self.LPFCurrentD.Tf) +";\n"
352 |
353 | if generateParams[5] or generateParams==[]:
354 | code += "// Limits \n"
355 | code += "motor.velocity_limit = " + str(self.velocityLimit) +";\n"
356 | code += "motor.voltage_limit = " + str(self.voltageLimit) +";\n"
357 | code += "motor.current_limit = " + str(self.currentLimit) +";\n"
358 |
359 | if generateParams[6] or generateParams==[]:
360 | code += "// sensor zero offset - home position \n"
361 | code += "motor.sensor_offset = " + str(self.sensorZeroOffset) +";\n"
362 |
363 | if generateParams[7] or generateParams==[]:
364 | code += "// sensor zero electrical angle \n"
365 | code += "// this parameter enables skipping a part of initFOC \n"
366 | code += "motor.sensor_electrical_offset = " + str(self.sensorElectricalZero) +";\n"
367 |
368 | if generateParams[8] or generateParams==[]:
369 | code += "// general settings \n"
370 | code += "// motor phase resistance \n"
371 | code += "motor.phase_resistance = " + str(self.sensorElectricalZero) +";\n"
372 |
373 | if generateParams[9] or generateParams==[]:
374 | code += "// pwm modulation settings \n"
375 | code += "motor.foc_modulation = FOCModulationType::"
376 | if self.modulationType == self.SINE_PWM:
377 | code += "SinePWM"
378 | elif self.modulationType == self.SPACE_VECTOR_PWM:
379 | code += "SpaceVectorPWM"
380 | elif self.modulationType == self.TRAPEZOIDAL_120:
381 | code += "Trapezoid_120"
382 | elif self.modulationType == self.TRAPEZOIDAL_150:
383 | code += "Trapezoid_150"
384 | code += ";\n"
385 | code += "motor.modulation_centered = " + str(self.modulationCentered) +";\n"
386 |
387 | # code += "\n\nmotor.init();\nmotor.initFOC();\n\n...\n\n }"
388 | # code += "\n\nvoid loop() {\n\n....\n\n}"
389 |
390 | return code
391 |
392 | def __initCommunications(self):
393 | self.serialPort = serial.Serial(self.serialPortName,
394 | self.serialRate,
395 | self.serialByteSize,
396 | self.serialParity,
397 | self.stopBits)
398 |
399 | self.commProvider.serialComm = self.serialPort
400 | self.commProvider.start()
401 |
402 | def __closeCommunication(self):
403 | self.serialPort.close()
404 |
405 | def connect(self, connectionMode):
406 | try:
407 | self.__initCommunications()
408 | except SerialException as serEx:
409 | logging.warning('Is not possible to open serial port')
410 | logging.warning('Port =' + self.serialPortName)
411 | logging.warning('Rate =' + str(self.serialRate))
412 | logging.warning('parity =' + str(self.serialParity))
413 | logging.warning('Byte size =' + str(self.serialByteSize))
414 | logging.warning('Stop bits=' + str(self.stopBits))
415 |
416 | msgBox = QtWidgets.QMessageBox()
417 | msgBox.setIcon(QtWidgets.QMessageBox.Warning)
418 | msgBox.setText('打开串口时出错')
419 | msgBox.setWindowTitle('SimpleFOC ConfigTool')
420 | msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
421 | msgBox.exec()
422 | return False
423 | else:
424 | self.isConnected = True
425 | for listener in self.connectionStateListenerList:
426 | listener.connectionStateChanged(True)
427 | if connectionMode == SimpleFOCDevice.PULL_CONFIG_ON_CONNECT:
428 | self.pullConfiguration()
429 | if self.stateUpdater.stopped():
430 | self.stateUpdater = StateUpdateRunner(self)
431 | self.stateUpdater.start()
432 | elif connectionMode == SimpleFOCDevice.PUSH_CONFG_ON_CONNECT:
433 | self.pushConfiguration()
434 | if self.stateUpdater.stopped():
435 | self.stateUpdater = StateUpdateRunner(self)
436 | self.stateUpdater.start()
437 | pass
438 | return True
439 |
440 | def disConnect(self):
441 | self.isConnected = False
442 | self.__closeCommunication()
443 | self.stateUpdater.stop()
444 | for listener in self.connectionStateListenerList:
445 | listener.connectionStateChanged(False)
446 |
447 | def addConnectionStateListener(self, listener):
448 | self.connectionStateListenerList.append(listener)
449 |
450 | def sendCommand(self, command):
451 | if self.isConnected:
452 | self.serialPort.write((str(command) + '\n').encode('utf-8'))
453 |
454 | def setCommand(self, command, value):
455 | if self.isConnected:
456 | self.sendCommand(str(self.devCommandID) + str(command) + str(value))
457 |
458 | def getCommand(self, command):
459 | if self.isConnected:
460 | self.sendCommand(str(self.devCommandID) + str(command) )
461 |
462 | def sendControlType(self, loop_control_type):
463 | if self.isConnected:
464 | if loop_control_type != '':
465 | self.controlType = loop_control_type
466 | self.setCommand('C', str(loop_control_type))
467 |
468 | def sendTorqueType(self, torque_type):
469 | if self.isConnected:
470 | if torque_type != '':
471 | self.torqueType = torque_type
472 | self.setCommand('T', str(torque_type))
473 |
474 | def sendMotionDownsample(self, value):
475 | if self.isConnected:
476 | if value != '':
477 | self.motionDownsample = value
478 | self.setCommand('CD', str(value))
479 |
480 | def sendProportionalGain(self, pid, value ):
481 | if self.isConnected:
482 | if value != '':
483 | pid.P = value
484 | self.setCommand(str(pid.cmd)+'P', str(value))
485 |
486 | def sendIntegralGain(self, pid, value):
487 | if self.isConnected:
488 | if value != '':
489 | pid.I = value
490 | self.setCommand(str(pid.cmd)+'I', str(value))
491 |
492 | def sendDerivativeGain(self, pid, value):
493 | if self.isConnected:
494 | if value != '':
495 | pid.D = value
496 | self.setCommand(str(pid.cmd)+'D', str(value))
497 |
498 | def sendOutputRamp(self, pid, value):
499 | if self.isConnected:
500 | if value != '':
501 | pid.outputRamp = value
502 | self.setCommand(str(pid.cmd)+'R', str(value))
503 |
504 | def sendOutputLimit(self, pid, value):
505 | if self.isConnected:
506 | if value != '':
507 | pid.outputLimit = value
508 | self.setCommand(str(pid.cmd)+'L', str(value))
509 |
510 | def sendLowPassFilter(self, lpf, value):
511 | if self.isConnected:
512 | if value != '':
513 | lpf.Tf = value
514 | self.setCommand(str(lpf.cmd)+'F', str(value))
515 |
516 | def sendVelocityLimit(self, value):
517 | if self.isConnected:
518 | if value != '':
519 | self.velocityLimit = value
520 | self.setCommand('LV', str(value))
521 |
522 | def sendVoltageLimit(self, value):
523 | if self.isConnected:
524 | if value != '':
525 | self.voltageLimit = value
526 | self.setCommand('LU', str(value))
527 |
528 | def sendCurrentLimit(self, value):
529 | if self.isConnected:
530 | if value != '':
531 | self.currentLimit = value
532 | self.setCommand('LC', str(value))
533 |
534 | def sendPhaseResistance(self, value):
535 | if self.isConnected:
536 | if value != '':
537 | self.phaseResistance = value
538 | self.setCommand('R', str(value))
539 |
540 | def sendTargetValue(self, targetvalue):
541 | if self.isConnected:
542 | if targetvalue != '':
543 | self.target = targetvalue
544 | self.setCommand('',self.target)
545 |
546 | def sendSensorZeroOffset(self, targetvalue):
547 | if self.isConnected:
548 | if targetvalue != '':
549 | self.sensorZeroOffset = targetvalue
550 | self.setCommand('SM', str(targetvalue))
551 |
552 | def sendSensorZeroElectrical(self, targetvalue):
553 | if self.isConnected:
554 | if targetvalue != '':
555 | self.sensorElectricalZero = targetvalue
556 | self.setCommand('SE', str(targetvalue))
557 |
558 | def sendDeviceStatus(self, targetvalue):
559 | if self.isConnected:
560 | if targetvalue != '':
561 | self.deviceStatus = targetvalue
562 | self.setCommand('E', str(targetvalue))
563 |
564 |
565 | def sendModulationCentered(self, targetvalue):
566 | if self.isConnected:
567 | if targetvalue != '':
568 | self.modulationCentered = targetvalue
569 | self.setCommand('WC', str(targetvalue))
570 |
571 | def sendModulationType(self, targetvalue):
572 | if self.isConnected:
573 | if targetvalue != '':
574 | self.modulationType = targetvalue
575 | self.setCommand('WT', str(targetvalue))
576 |
577 | def sendDeviceStatus(self, targetvalue):
578 | if self.isConnected:
579 | if targetvalue != '':
580 | self.deviceStatus = targetvalue
581 | self.setCommand('E', str(targetvalue))
582 |
583 | def sendMonitorDownsample(self, targetvalue):
584 | if self.isConnected:
585 | if targetvalue != '':
586 | self.monitorDownsample = targetvalue
587 | self.setCommand('MD', str(targetvalue))
588 |
589 | def sendMonitorClearVariables(self):
590 | if self.isConnected:
591 | self.monitorVariables = 0
592 | self.getCommand('MC')
593 |
594 | def sendMonitorVariables(self, vararray):
595 | if self.isConnected:
596 | if vararray != '':
597 | val = 0
598 | m = 10**6
599 | for var in vararray:
600 | val = val+ int(var)*m
601 | m = m/10
602 | self.monitorVariables = vararray
603 | self.setCommand('MS', "{:07d}".format(int(val)))
604 | else:
605 | self.getCommand('MS')
606 |
607 |
608 | def updateStates(self):
609 | if self.isConnected:
610 | self.getCommand('MG0')
611 | time.sleep(100 / 1000)
612 | self.getCommand('MG1')
613 | time.sleep(100 / 1000)
614 | self.getCommand('MG2')
615 | time.sleep(100 / 1000)
616 | self.getCommand('MG3')
617 | time.sleep(100 / 1000)
618 | self.getCommand('MG4')
619 | time.sleep(100 / 1000)
620 | self.getCommand('MG5')
621 | time.sleep(100 / 1000)
622 | self.getCommand('MG6')
623 | time.sleep(100 / 1000)
624 |
625 |
626 | def pushConfiguration(self):
627 | print("push")
628 | # self.sendControlType(self.controlType)
629 | # self.sendProportionalGain(self.PIDVelocity, self.self)
630 | # self.sendIntegralGain(self.PIDVelocity, self.integralGainPID)
631 | # self.sendDerivativeGain(self.PIDVelocity, self.derivativeGainPID)
632 | # self.sendOutputRamp(self.PIDVelocity, self.voltageRampPID)
633 | # self.sendLowPassFilter(self.LPFVelocity,self.lowPassFilter)
634 | # self.sendPGain(self.anglePGain)
635 | # self.sendVelocityLimit(self.velocityLimit)
636 | # self.sendVoltageLimit(self.voltageLimit)
637 | # self.sendTargetValue(self.initialTarget)
638 |
639 | def pullPIDConf(self, pid, lpf):
640 | self.sendProportionalGain(pid,'')
641 | time.sleep(5 / 1000)
642 | self.sendIntegralGain(pid,'')
643 | time.sleep(5 / 1000)
644 | self.sendDerivativeGain(pid,'')
645 | time.sleep(5 / 1000)
646 | self.sendOutputRamp(pid,'')
647 | time.sleep(5 / 1000)
648 | self.sendOutputLimit(pid,'')
649 | time.sleep(5 / 1000)
650 | self.sendLowPassFilter(lpf,'')
651 |
652 | def pullConfiguration(self):
653 | time.sleep(5 / 1000)
654 | self.sendControlType('')
655 | time.sleep(5 / 1000)
656 | self.sendTorqueType('')
657 | time.sleep(5 / 1000)
658 | self.pullPIDConf( self.PIDVelocity, self.LPFVelocity)
659 | time.sleep(5 / 1000)
660 | self.pullPIDConf( self.PIDAngle, self.LPFAngle)
661 | time.sleep(5 / 1000)
662 | self.pullPIDConf( self.PIDCurrentD, self.LPFCurrentD)
663 | time.sleep(5 / 1000)
664 | self.pullPIDConf( self.PIDCurrentQ, self.LPFCurrentQ)
665 | time.sleep(5 / 1000)
666 | self.sendVelocityLimit('')
667 | time.sleep(5 / 1000)
668 | self.sendVoltageLimit('')
669 | time.sleep(5 / 1000)
670 | self.sendCurrentLimit('')
671 | time.sleep(5 / 1000)
672 | self.sendSensorZeroElectrical('')
673 | time.sleep(5 / 1000)
674 | self.sendSensorZeroOffset('')
675 | time.sleep(5 / 1000)
676 | self.sendMotionDownsample('')
677 | time.sleep(5 / 1000)
678 | self.sendPhaseResistance('')
679 | time.sleep(5 / 1000)
680 | self.sendModulationCentered('')
681 | time.sleep(5 / 1000)
682 | self.sendModulationCentered('')
683 | time.sleep(5 / 1000)
684 | self.sendDeviceStatus('')
685 |
686 | def parsePIDFResponse(self, pid, lpf, comandResponse):
687 | if 'P' in comandResponse:
688 | pid.P = float(comandResponse.replace('P: ', ''))
689 | if 'I' in comandResponse:
690 | pid.I = float(comandResponse.replace('I: ', ''))
691 | if 'D' in comandResponse:
692 | pid.D = float(comandResponse.replace('D: ', ''))
693 | if 'ramp' in comandResponse:
694 | val = comandResponse.replace('ramp:', '')
695 | if 'ovf' in val:
696 | pid.outputRamp = 0
697 | else:
698 | pid.outputRamp = float(comandResponse.replace('ramp:', ''))
699 | if 'limit' in comandResponse:
700 | pid.outputLimit = float(comandResponse.replace('limit:', ''))
701 | if 'Tf' in comandResponse:
702 | lpf.Tf = float(comandResponse.replace('Tf: ', ''))
703 |
704 | def parseLimitsResponse(self, comandResponse):
705 | if 'vel:' in comandResponse:
706 | self.velocityLimit = float(comandResponse.replace('vel:', ''))
707 | elif 'volt:' in comandResponse:
708 | self.voltageLimit = float(comandResponse.replace('volt:', ''))
709 | elif 'curr:' in comandResponse:
710 | self.currentLimit = float(comandResponse.replace('curr:', ''))
711 |
712 | def parseMotionResponse(self, comandResponse):
713 | if 'downsample' in comandResponse:
714 | self.motionDownsample = float(comandResponse.replace('downsample:', ''))
715 | elif 'torque' in comandResponse:
716 | self.controlType = 0
717 | elif 'angle open' in comandResponse:
718 | self.controlType = 4
719 | elif 'angle' in comandResponse:
720 | self.controlType = 2
721 | elif 'vel open' in comandResponse:
722 | self.controlType = 3
723 | elif 'vel' in comandResponse:
724 | self.controlType = 1
725 |
726 | def parsePWMModResponse(self, comandResponse):
727 | if 'center' in comandResponse:
728 | self.modulationCentered = float(comandResponse.replace('center:', ''))
729 | elif 'type' in comandResponse:
730 | comandResponse = comandResponse.replace('type:', '')
731 | if 'Sine' in comandResponse:
732 | self.modulationType = self.SINE_PWM
733 | elif 'SVPWM' in comandResponse:
734 | self.modulationType = self.SPACE_VECTOR_PWM
735 | elif 'Trap 120' in comandResponse:
736 | self.modulationType = self.TRAPEZOIDAL_120
737 | elif 'Trap 150' in comandResponse:
738 | self.modulationType = self.TRAPEZOIDAL_150
739 |
740 | def parseTorqueResponse(self, comandResponse):
741 | if 'volt' in comandResponse:
742 | self.torqueType = 0
743 | elif 'dc curr' in comandResponse:
744 | self.torqueType = 1
745 | elif 'foc curr' in comandResponse:
746 | self.torqueType = 2
747 |
748 | def parseSensorResponse(self, comandResponse):
749 | if 'el. offset' in comandResponse:
750 | self.sensorElectricalZero = float(comandResponse.replace('el. offset:', ''))
751 | elif 'offset' in comandResponse:
752 | self.sensorZeroOffset = float(comandResponse.replace('offset:', ''))
753 |
754 | def parseMonitorResponse(self, comandResponse):
755 | if 'all' in comandResponse:
756 | varStr = comandResponse.replace('all:', '')
757 | states = varStr.rstrip().split('\t', 7)
758 | self.targetNow = states[0]
759 | self.voltageQNow = states[1]
760 | self.voltageDNow = states[2]
761 | self.currentQNow = states[3]
762 | self.currentDNow = states[4]
763 | self.velocityNow = states[5]
764 | self.angleNow = states[6]
765 | if 'target' in comandResponse:
766 | self.targetNow = float(comandResponse.replace('target:', ''))
767 | elif 'Vq' in comandResponse:
768 | self.voltageQNow = float(comandResponse.replace('Vq:', ''))
769 | elif 'Vd' in comandResponse:
770 | self.voltageDNow = float(comandResponse.replace('Vd:', ''))
771 | elif 'Cq' in comandResponse:
772 | self.currentQNow = float(comandResponse.replace('Cq:', ''))
773 | elif 'Cd' in comandResponse:
774 | self.currentDNow = float(comandResponse.replace('Cd:', ''))
775 | elif 'vel' in comandResponse:
776 | self.velocityNow = float(comandResponse.replace('vel:', ''))
777 | elif 'angle' in comandResponse:
778 | self.angleNow = float(comandResponse.replace('angle:', ''))
779 |
780 | def parseResponses(self, comandResponse):
781 | if 'PID vel' in comandResponse:
782 | comandResponse = comandResponse.replace('PID vel|', '')
783 | self.parsePIDFResponse(self.PIDVelocity, self.LPFVelocity, comandResponse)
784 | elif 'PID angle' in comandResponse:
785 | comandResponse = comandResponse.replace('PID angle|', '')
786 | self.parsePIDFResponse(self.PIDAngle, self.LPFAngle, comandResponse)
787 | elif 'PID curr q' in comandResponse:
788 | comandResponse = comandResponse.replace('PID curr q|', '')
789 | self.parsePIDFResponse(self.PIDCurrentQ, self.LPFCurrentQ, comandResponse)
790 | elif 'PID curr d' in comandResponse:
791 | comandResponse = comandResponse.replace('PID curr d|', '')
792 | self.parsePIDFResponse(self.PIDCurrentD, self.LPFCurrentD, comandResponse)
793 | elif 'Limits' in comandResponse:
794 | comandResponse = comandResponse.replace('Limits|', '')
795 | self.parseLimitsResponse(comandResponse)
796 | elif 'Motion' in comandResponse:
797 | comandResponse = comandResponse.replace('Motion:', '')
798 | self.parseMotionResponse(comandResponse)
799 | elif 'Torque' in comandResponse:
800 | comandResponse = comandResponse.replace('Torque:', '')
801 | self.parseTorqueResponse(comandResponse)
802 | elif 'Sensor' in comandResponse:
803 | comandResponse = comandResponse.replace('Sensor |', '')
804 | self.parseSensorResponse(comandResponse)
805 | elif 'Monitor' in comandResponse:
806 | comandResponse = comandResponse.replace('Monitor |', '')
807 | self.parseMonitorResponse(comandResponse)
808 | elif 'Status' in comandResponse:
809 | self.deviceStatus = float(comandResponse.replace('Status:', ''))
810 | elif 'R phase' in comandResponse:
811 | self.phaseResistance = float(comandResponse.replace('R phase:', ''))
812 | elif 'PWM Mod' in comandResponse:
813 | comandResponse = comandResponse.replace('PWM Mod | ', '')
814 | self.parsePWMModResponse(comandResponse)
815 |
816 | def parseStateResponses(self, comandResponse):
817 | if 'Monitor' in comandResponse:
818 | comandResponse = comandResponse.replace('Monitor |', '')
819 | self.parseMonitorResponse(comandResponse)
820 |
821 |
822 | class SerialPortReceiveHandler(QtCore.QThread):
823 | monitoringDataReceived = QtCore.pyqtSignal(list)
824 | commandDataReceived = QtCore.pyqtSignal(str)
825 | stateMonitorReceived = QtCore.pyqtSignal(str)
826 | rawDataReceived = QtCore.pyqtSignal(str)
827 |
828 | def __init__(self, serial_port=None, *args,**kwargs):
829 | super(SerialPortReceiveHandler, self).__init__(*args, **kwargs)
830 | self._stop_event = threading.Event()
831 | self.serialComm = serial_port
832 |
833 | def handle_received_data(self, data):
834 | if not data:
835 | return
836 |
837 | if self.isDataReceivedMonitoring(data):
838 | try:
839 | v = data.rstrip().split('\t')
840 | self.monitoringDataReceived.emit(v)
841 | except ValueError as error:
842 | logging.error(error, exc_info=True)
843 | logging.error('data =' + str(data), exc_info=True)
844 | except IndexError as error:
845 | logging.error(error, exc_info=True)
846 | logging.error('data =' + str(data), exc_info=True)
847 | elif self.isDataReceivedStates(data):
848 | self.stateMonitorReceived.emit(data.rstrip())
849 | else:
850 | self.commandDataReceived.emit(data.rstrip())
851 | self.rawDataReceived.emit(data.rstrip())
852 |
853 | def isDataReceivedMonitoring(self, data):
854 | if data[0].isdigit() or data[0] == '-':
855 | return True
856 | else:
857 | return False
858 |
859 | def isDataReceivedStates(self, data):
860 | if 'Monitor' in data:
861 | return True
862 | else:
863 | return False
864 |
865 | def run(self):
866 | try:
867 | while not self.stopped():
868 | if self.serialComm is not None:
869 | if self.serialComm.isOpen():
870 | reading = self.serialComm.readline()
871 | if reading:
872 | self.handle_received_data(reading.decode())
873 | except SerialException as serialException:
874 | logging.error(serialException, exc_info=True)
875 | except TypeError as typeError:
876 | logging.error(typeError, exc_info=True)
877 | except AttributeError as ae:
878 | logging.error(ae, exc_info=True)
879 |
880 | def stop(self):
881 | self._stop_event.set()
882 |
883 | def stopped(self):
884 | return self._stop_event.is_set()
885 |
886 |
887 | class StateUpdateRunner(QtCore.QThread):
888 |
889 | def __init__(self, connector=None, *args,**kwargs):
890 | super(StateUpdateRunner, self).__init__(*args, **kwargs)
891 | self._stop_event = threading.Event()
892 | self.deviceConnector = connector
893 | def run(self):
894 | try:
895 | while not self.stopped():
896 | if self.deviceConnector is not None:
897 | if self.deviceConnector.commProvider.serialComm.isOpen():
898 | self.deviceConnector.updateStates()
899 | time.sleep(1)
900 | except SerialException as serialException:
901 | logging.error(serialException, exc_info=True)
902 |
903 | def stop(self):
904 | self._stop_event.set()
905 |
906 | def stopped(self):
907 | return self._stop_event.is_set()
908 |
--------------------------------------------------------------------------------