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 text: modelData
183 checkable: true
184 }
185
186 QGCColoredImage {
187 id: deleteIcon
188 anchors.left: button.right
189 anchors.verticalCenter: button.verticalCenter
190 width: height
191 height: button._iconHeight
192 fillMode: Image.PreserveAspectFit
193 source: "/res/TrashCan.svg"
194 color: button.textColor
195 visible: button.checked
196
197 QGCMouseArea {
198 fillItem: parent
199 onClicked: {
200 var confirm = qsTr("Are you sure you want to delete the script \"%1\"? This action cannot be undone.").arg(modelData)
201 QGroundControl.showMessageDialog(scriptingPage, qsTr("Delete Lua Script"), confirm, Dialog.Ok | Dialog.Cancel, function() {
202 var remotePath = root.fullRemotePath(modelData)
203 if (!ftpController.deleteFile(remotePath)) {
204 var deleteError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Delete failed")
205 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Delete"), deleteError)
206 }
207 })
208 }
209 }
210 }
211 }
212 }
213 }
214
215 RowLayout {
216 Layout.fillWidth: true
217 spacing: ScreenTools.defaultFontPixelHeight * 0.5
218
219 QGCButton {
220 text: qsTr("Cancel Operation")
221 visible: ftpController.busy
222 onClicked: ftpController.cancelActiveOperation()
223 }
224
225 Item { Layout.fillWidth: true }
226
227 QGCLabel {
228 text: qsTr("Transferring... %1%" ).arg(Math.round(ftpController.progress * 100))
229 visible: ftpController.busy
230 }
231 }
232
233 QGCFileDialog {
234 id: uploadDialog
235 title: qsTr("Select Lua script to upload")
236 nameFilters: root.luaNameFilters
237
238 onAcceptedForLoad: (file) => {
239 if (!file) {
240 close()
241 return
242 }
243 var fileName = root.fileNameFromPath(file)
244 if (fileName.length === 0) {
245 close()
246 return
247 }
248 if (!fileName.toLowerCase().endsWith(".lua")) {
249 fileName = fileName + ".lua"
250 }
251 var remotePath = root.fullRemotePath(fileName)
252 if (!ftpController.uploadFile(file, remotePath)) {
253 var uploadError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Upload failed")
254 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Upload"), uploadError)
255 }
256 close()
257 }
258 }
259
260 QGCFileDialog {
261 id: downloadDialog
262 title: qsTr("Save Lua Script")
263 nameFilters: root.luaNameFilters
264 selectFolder: true
265
266 property string fileToDownload
267
268 onAcceptedForLoad: (folder) => {
269 if (!folder) {
270 close()
271 return
272 }
273 if (!ftpController.downloadFile(root.fullRemotePath(fileToDownload), folder, fileToDownload)) {
274 var downloadError = ftpController.errorString.length > 0 ? ftpController.errorString : qsTr("Download failed")
275 QGroundControl.showMessageDialog(scriptingPage, qsTr("Lua Download"), downloadError)
276 }
277 close()
278 }
279 }
280
281 Rectangle {
282 Layout.fillWidth: true
283 Layout.preferredHeight: availableHeight
284 color: qgcPal.window
285 visible: scriptingEnabledFact === null
286
287 QGCLabel {
288 anchors.centerIn: parent
289 text: qsTr("Scripting is not supported by this version of firmware.")
290 wrapMode: Text.Wrap
291 horizontalAlignment: Text.AlignHCenter
292 verticalAlignment: Text.AlignVCenter
293 }
294 }
295 }
296 }
297}