QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ScriptingComponent.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Layouts
4
5import QGroundControl
6import QGroundControl.Controls
7import QGroundControl.FactControls
8
9SetupPage {
10 id: scriptingPage
11 pageComponent: pageComponent
12
13 Component {
14 id: pageComponent
15
16 ColumnLayout {
17 id: root
18 width: availableWidth
19 spacing: ScreenTools.defaultFontPixelHeight * 0.75
20
21 readonly property string scriptRoot: "/APM/scripts/"
22 readonly property var luaNameFilters: [ qsTr("Lua Scripts (*.lua)"), qsTr("All Files (*)") ]
23 readonly property Fact scriptingEnabledFact: factController.getParameterFact(-1, "SCR_ENABLE", false)
24
25 readonly property var filteredEntries: {
26 let rawEntries = ftpController.directoryEntries.filter(function(entry) {
27 if (!entry || entry.length < 2) {
28 return false
29 }
30 var kind = entry.charAt(0)
31 if (kind === "F") {
32 return true
33 }
34 return false
35 })
36 let filenameEntries = []
37 for (let i=0; i<rawEntries.length; i++) {
38 filenameEntries.push(rawEntries[i].slice(1).split("\t")[0])
39 }
40 return filenameEntries
41 }
42
43 function fullRemotePath(filename) {
44 return scriptRoot + filename
45 }
46
47 function fileNameFromPath(path) {
48 if (!path) {
49 return ""
50 }
51 // Handle both forward slashes and backslashes
52 var normalizedPath = path.replace(/\\/g, "/")
53 var parts = normalizedPath.split("/")
54 return parts[parts.length - 1]
55 }
56
57 function refreshDirectoryList() {
58 ftpController.listDirectory(scriptRoot)
59 }
60
61 Component.onCompleted: {
62 if (scriptingEnabledFact && scriptingEnabledFact.rawValue) {
63 ftpController.listDirectory(root.scriptRoot)
64 }
65 }
66
67 Connections {
68 target: scriptingEnabledFact
69
70 onRawValueChanged: {
71 if (scriptingEnabledFact.rawValue) {
72 ftpController.listDirectory(root.scriptRoot)
73 } else {
74 statusText.text = ""
75 }
76 }
77 }
78
79 FTPController {
80 id: ftpController
81
82 onUploadComplete: (remotePath, error) => {
83 if (error.length > 0) {
84 statusText.text = error
85 } else {
86 statusText.text = qsTr("Upload succeeded: %1").arg(remotePath)
87 refreshDirectoryList()
88 }
89 }
90
91 onDownloadComplete: (filePath, error) => {
92 if (error.length > 0) {
93 statusText.text = error
94 } else {
95 statusText.text = qsTr("Download succeeded: %1").arg(filePath)
96 }
97 }
98
99 onDeleteComplete: (remotePath, error) => {
100 if (error.length > 0) {
101 statusText.text = error
102 } else {
103 statusText.text = qsTr("Delete succeeded: %1").arg(remotePath)
104 refreshDirectoryList()
105 }
106 }
107 }
108
109 FactPanelController {
110 id: factController
111 }
112
113 QGCPalette { id: qgcPal; colorGroupEnabled: true }
114
115 FactCheckBoxSlider {
116 text: qsTr("Enable Scripting")
117 fact: scriptingEnabledFact
118 enabled: scriptingEnabledFact !== null
119 }
120
121 QGCLabel {
122 id: statusText
123 Layout.fillWidth: true
124 text: ftpController.errorString
125 wrapMode: Text.Wrap
126 }
127
128 Flow {
129 id: directoryView
130 Layout.fillWidth: true
131 spacing: ScreenTools.defaultFontPixelHeight
132 enabled: !ftpController.busy && scriptingEnabledFact && scriptingEnabledFact.rawValue
133
134 QGCButton {
135 text: qsTr("Upload")
136 iconSource: "/res/Upload.svg"
137 enabled: !ftpController.busy
138 onClicked: {
139 uploadDialog.folder = QGroundControl.settingsManager.appSettings.missionSavePath
140 uploadDialog.openForLoad()
141 }
142 }
143
144 Repeater {
145 model: root.filteredEntries
146
147 Rectangle {
148 width: button.checked ? deleteIcon.x + deleteIcon.width + button.rightPadding : button.width
149 height: button.height
150 radius: button.backRadius
151 border.width: button.showBorder ? 1 : 0
152 border.color: button.background.border.color
153 color: qgcPal.buttonHighlight
154
155 QGCColoredImage {
156 id: downloadIcon
157 anchors.leftMargin: button.leftPadding
158 anchors.left: parent.left
159 anchors.verticalCenter: button.verticalCenter
160 width: height
161 height: button._iconHeight
162 fillMode: Image.PreserveAspectFit
163 source: "/res/Download.svg"
164 color: button.textColor
165 visible: button.checked
166
167 QGCMouseArea {
168 fillItem: parent
169 onClicked: {
170 downloadDialog.defaultSuffix = "lua"
171 downloadDialog.title = qsTr("Download %1").arg(modelData)
172 downloadDialog.fileToDownload = modelData
173 downloadDialog.folder = QGroundControl.settingsManager.appSettings.missionSavePath
174 downloadDialog.openForLoad()
175 }
176 }
177 }
178
179 QGCButton {
180 id: button
181 anchors.left: checked ? downloadIcon.right : parent.left
182 anchors.leftMargin: checked ? leftPadding : 0
183 text: modelData
184 checkable: true
185 }
186
187 QGCColoredImage {
188 id: deleteIcon
189 anchors.left: button.right
190 anchors.leftMargin: button.rightPadding
191 anchors.verticalCenter: button.verticalCenter
192 width: height
193 height: button._iconHeight
194 fillMode: Image.PreserveAspectFit
195 source: "/res/TrashCan.svg"
196 color: button.textColor
197 visible: button.checked
198
199 QGCMouseArea {
200 fillItem: parent
201 onClicked: {
202 var confirm = qsTr("Are you sure you want to delete the script \"%1\"? This action cannot be undone.").arg(modelData)
203 QGroundControl.showMessageDialog(scriptingPage, qsTr("Delete Lua Script"), confirm, Dialog.Ok | Dialog.Cancel, function() {
204 var remotePath = root.fullRemotePath(modelData)
205 if (!ftpController.deleteFile(remotePath)) {
206 var deleteError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Delete failed")
207 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Delete"), deleteError)
208 }
209 })
210 }
211 }
212 }
213 }
214 }
215 }
216
217 RowLayout {
218 Layout.fillWidth: true
219 spacing: ScreenTools.defaultFontPixelHeight * 0.5
220
221 QGCButton {
222 text: qsTr("Cancel Operation")
223 visible: ftpController.busy
224 onClicked: ftpController.cancelActiveOperation()
225 }
226
227 Item { Layout.fillWidth: true }
228
229 QGCLabel {
230 text: qsTr("Transferring... %1%" ).arg(Math.round(ftpController.progress * 100))
231 visible: ftpController.busy
232 }
233 }
234
235 QGCFileDialog {
236 id: uploadDialog
237 title: qsTr("Select Lua script to upload")
238 nameFilters: root.luaNameFilters
239
240 onAcceptedForLoad: (file) => {
241 if (!file) {
242 close()
243 return
244 }
245 var fileName = root.fileNameFromPath(file)
246 if (fileName.length === 0) {
247 close()
248 return
249 }
250 if (!fileName.toLowerCase().endsWith(".lua")) {
251 fileName = fileName + ".lua"
252 }
253 var remotePath = root.fullRemotePath(fileName)
254 if (!ftpController.uploadFile(file, remotePath)) {
255 var uploadError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Upload failed")
256 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Upload"), uploadError)
257 }
258 close()
259 }
260 }
261
262 QGCFileDialog {
263 id: downloadDialog
264 title: qsTr("Save Lua Script")
265 nameFilters: root.luaNameFilters
266 selectFolder: true
267
268 property string fileToDownload
269
270 onAcceptedForLoad: (folder) => {
271 if (!folder) {
272 close()
273 return
274 }
275 if (!ftpController.downloadFile(root.fullRemotePath(fileToDownload), folder, fileToDownload)) {
276 var downloadError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Download failed")
277 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Download"), downloadError)
278 }
279 close()
280 }
281 }
282
283 Rectangle {
284 Layout.fillWidth: true
285 Layout.preferredHeight: availableHeight
286 color: qgcPal.window
287 visible: scriptingEnabledFact === null
288
289 QGCLabel {
290 anchors.centerIn: parent
291 text: qsTr("Scripting is not supported by this version of firmware.")
292 wrapMode: Text.Wrap
293 horizontalAlignment: Text.AlignHCenter
294 verticalAlignment: Text.AlignVCenter
295 }
296 }
297 }
298 }
299}