Skip to content

Commit f29483f

Browse files
author
Sascha Müller zum Hagen
committed
Save and load within folder and create new ones.
1 parent 382841b commit f29483f

File tree

8 files changed

+373
-353
lines changed

8 files changed

+373
-353
lines changed

code/examples/Test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print('Hello HuCon!')

code/examples/Test.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<xml xmlns="http://www.w3.org/1999/xhtml">
2+
<block type="text_print" id="!?|L[H[0sBL^t9sJ7d#w" x="113" y="63">
3+
<value name="TEXT">
4+
<shadow type="text" id="DTPr.5v+/9d1tUJbjQo(">
5+
<field name="TEXT">Hello HuCon!</field>
6+
</shadow>
7+
</value>
8+
</block>
9+
</xml>

webserver/HuConJsonRpc.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def handle_control(cls, rpc_request):
7070
return cls._poll(rpc_request)
7171
elif rpc_request['method'] == 'get_file_list':
7272
return cls._get_file_list(rpc_request)
73+
elif rpc_request['method'] == 'create_folder':
74+
return cls._create_folder(rpc_request)
7375
elif rpc_request['method'] == 'load_file':
7476
return cls._load_file(rpc_request)
7577
elif rpc_request['method'] == 'save_file':
@@ -124,7 +126,7 @@ def _replace_hucon_requests(cls, message):
124126
Print an answer from HuCon whenever the the message 'Hello HoCon!' is found.
125127
"""
126128
search_string = 'print(\'Hello HuCon!\')'
127-
replace_string = 'print(\'Hello HuCon!\\n\\nHello human!\\nI am a human controlled robot.\\n\\n\')'
129+
replace_string = 'print(\'Hello HuCon!\\n\\nHello human!\\nI am a Hu[man] Con[trolled] robot.\\n\')'
128130
if search_string in message:
129131
message = message.replace(search_string, replace_string)
130132
return message
@@ -203,6 +205,23 @@ def _get_file_list(cls, rpc_request):
203205
else:
204206
return json_dump
205207

208+
def _create_folder(cls, rpc_request):
209+
"""
210+
Creates the folder on the device.
211+
"""
212+
try:
213+
new_folder = os.path.join(cls._CODE_ROOT, rpc_request['params'].strip('/\\'))
214+
215+
if not os.path.exists(new_folder):
216+
os.makedirs(new_folder)
217+
218+
rpc_response = cls._get_rpc_response(rpc_request['id'])
219+
json_dump = json.dumps(rpc_response)
220+
except Exception as e:
221+
return cls._return_error(rpc_request['id'], 'Could not create the folder. (%s)' % str(e))
222+
else:
223+
return json_dump
224+
206225
def _load_file(cls, rpc_request):
207226
"""
208227
Return the content of the file back to the browser.

webserver/static/js/webserver.js

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
// Ask the user to go before he goes.
2+
$(window).bind("beforeunload", function(){
3+
if (HuConApp.UnsavedContent) {
4+
return "Do you really want to go?\nAll your unsaved data will be lost.";
5+
}
6+
});
7+
18
// On document ready this function will be called and do a base initialization.
29
$(document).ready(function () {
310
// Update the version information on the ui
411
HuConApp.updateVersion();
12+
13+
$('#loadModal').modal({allowMultiple: true});
14+
$('#newFolderModal').modal('attach events', '#newFolderButton')
15+
;
16+
;
517
});
618

719
// Handle some key bindings
@@ -38,6 +50,15 @@ var HuConApp = {}
3850
// Id for JSON-RPC protocol
3951
HuConApp.RpcId = 0;
4052

53+
// Folder path to save and load on the device
54+
HuConApp.Folder = '';
55+
56+
// File extension which is currently used.
57+
HuConApp.FileExt = '';
58+
59+
// Remeber any changes after save.
60+
HuConApp.UnsavedContent = false;
61+
4162
// Get a new JSON-RPC message data with a new id.
4263
HuConApp.getRpcRequest = function() {
4364
jsonRpc = {};
@@ -311,3 +332,271 @@ HuConApp.buttonModal = function() {
311332
$('#buttonModal').modal('show');
312333
$('#buttonEmbed').embed();
313334
}
335+
336+
// Set the breadcrump based on the current folder.
337+
HuConApp.setBreadcrumb = function(modal) {
338+
modal.html('<div class="divider">/</div>');
339+
340+
var folderPath = HuConApp.Folder.split('/');
341+
for (var i = 0; i < folderPath.length; i++) {
342+
if (folderPath[i] != '') {
343+
modal.append('<div class="section">' + folderPath[i] + '</div>');
344+
modal.append('<div class="divider">/</div>');
345+
}
346+
}
347+
}
348+
349+
// Show a form to create a new folder.
350+
HuConApp.createNewFolder = function() {
351+
foldername = $('#folderFilename').val();
352+
353+
if (foldername != '') {
354+
var rpcRequest = HuConApp.getRpcRequest();
355+
rpcRequest['method'] = 'create_folder';
356+
rpcRequest['params'] = HuConApp.Folder + '/' + foldername;
357+
358+
$.ajax('/API', {
359+
method: 'POST',
360+
data: JSON.stringify(rpcRequest),
361+
dataType: 'json',
362+
error: HuConApp.appendErrorLog
363+
});
364+
365+
HuConApp.saveFileModal(HuConApp.Folder);
366+
}
367+
console.log($('#folderFilename'));
368+
}
369+
370+
// Show the file open modal and list all folder.
371+
HuConApp.openFileModal = function(newFolder) {
372+
373+
if (newFolder != undefined) {
374+
HuConApp.Folder = newFolder;
375+
}
376+
377+
HuConApp.setBreadcrumb($('#openBreadcrumb'));
378+
379+
var rpcRequest = HuConApp.getRpcRequest();
380+
rpcRequest['method'] = 'get_file_list';
381+
rpcRequest['params'] = HuConApp.Folder;
382+
383+
$.ajax('/API', {
384+
method: 'POST',
385+
data: JSON.stringify(rpcRequest),
386+
dataType: 'json',
387+
success: function(rpcResponse) {
388+
HuConApp.configureLoadSaveModal(rpcResponse, $('#openFileList'), 'openFileModal', true);
389+
390+
$('#openModal').modal('show');
391+
},
392+
error: function(request, status, error) {
393+
HuConApp.Folder = '';
394+
HuConApp.appendErrorLog(request, status, error);
395+
}
396+
});
397+
}
398+
399+
// Hide the open file modal and load the content from the file.
400+
HuConApp.loadFileFromDevice = function(filename) {
401+
$('#openModal').modal('hide');
402+
$('#consoleLog').html('');
403+
404+
var rpcRequest = HuConApp.getRpcRequest();
405+
rpcRequest['method'] = 'load_file';
406+
rpcRequest['params'] = HuConApp.Folder + '/' + filename;
407+
408+
// Store the filename for the save dialog
409+
$('#saveFilename').val(filename);
410+
411+
$.ajax('/API', {
412+
method: 'POST',
413+
data: JSON.stringify(rpcRequest),
414+
dataType: 'json',
415+
success: function(rpcResponse) {
416+
if (HuConApp.isResponseError(rpcResponse)) {
417+
return
418+
}
419+
420+
if (rpcResponse['result']) {
421+
setFileContent(rpcResponse['result']);
422+
423+
HuConApp.appendConsoleLog('File "' + HuConApp.Folder + '/' + filename + '" loaded from local device.', 'green');
424+
425+
HuConApp.UnsavedContent = false;
426+
}
427+
},
428+
error: HuConApp.appendErrorLog
429+
});
430+
}
431+
432+
// Show the save file modal and load the file list from the device.
433+
HuConApp.saveFileModal = function(newFolder) {
434+
435+
// Set the new Folder
436+
if (newFolder != undefined) {
437+
HuConApp.Folder = newFolder;
438+
}
439+
440+
// The example folder is read only, so go back to root on save.
441+
if (HuConApp.Folder == '/examples') {
442+
HuConApp.Folder = '';
443+
}
444+
445+
HuConApp.setBreadcrumb($('#saveBreadcrumb'));
446+
447+
var rpcRequest = HuConApp.getRpcRequest();
448+
rpcRequest['method'] = 'get_file_list';
449+
rpcRequest['params'] = HuConApp.Folder;
450+
451+
$.ajax('/API', {
452+
method: 'POST',
453+
data: JSON.stringify(rpcRequest),
454+
dataType: 'json',
455+
success: function(rpcResponse) {
456+
HuConApp.configureLoadSaveModal(rpcResponse, $('#saveFileList'), 'saveFileModal', false);
457+
458+
$('#saveModal').modal('show');
459+
},
460+
error: function(request, status, error) {
461+
HuConApp.Folder = '';
462+
HuConApp.appendErrorLog(request, status, error);
463+
}
464+
});
465+
}
466+
467+
// Save the file on the device and hide the file save modal.
468+
HuConApp.saveFileOnDevice = function() {
469+
$('#saveModal').modal('hide');
470+
471+
$('#consoleLog').html('');
472+
473+
// Abort on write within the example folder
474+
if (HuConApp.Folder == '/examples') {
475+
HuConApp.appendConsoleLog('The file is not written on device.\nThe examples folder is read only!', 'red');
476+
return;
477+
}
478+
479+
// Get the filename without any extension.
480+
filename = $('#saveFilename').val();
481+
if (filename.substr(-HuConApp.FileExt.length) == HuConApp.FileExt) {
482+
filename = filename.slice(0, -HuConApp.FileExt.length);
483+
}
484+
$('#saveFilename').val(filename + HuConApp.FileExt);
485+
486+
var rpcRequest = HuConApp.getRpcRequest();
487+
rpcRequest['method'] = 'save_file';
488+
rpcRequest['params'] = {};
489+
490+
// Store the blockly code if needed
491+
if (HuConApp.FileExt == '.xml') {
492+
rpcRequest['params']['filename'] = HuConApp.Folder + '/' + filename + '.xml';
493+
rpcRequest['params']['data'] = getBlocklyCode();
494+
$.ajax('/API', {
495+
method: 'POST',
496+
data: JSON.stringify(rpcRequest),
497+
dataType: 'json',
498+
success: function (rpcResponse) {
499+
if (HuConApp.isResponseError(rpcResponse)) {
500+
return
501+
}
502+
503+
HuConApp.appendConsoleLog(rpcResponse['result']);
504+
},
505+
error: HuConApp.appendErrorLog
506+
});
507+
}
508+
509+
// Store the python code
510+
rpcRequest['params']['filename'] = HuConApp.Folder + '/' + filename + '.py';
511+
rpcRequest['params']['data'] = getPythonCode();
512+
$.ajax('/API', {
513+
method: 'POST',
514+
data: JSON.stringify(rpcRequest),
515+
dataType: 'json',
516+
success: function (rpcResponse) {
517+
if (HuConApp.isResponseError(rpcResponse)) {
518+
return
519+
}
520+
521+
HuConApp.appendConsoleLog(rpcResponse['result']);
522+
},
523+
error: HuConApp.appendErrorLog
524+
});
525+
526+
HuConApp.UnsavedContent = false;
527+
}
528+
529+
// Configure the load or save modal correctly.
530+
HuConApp.configureLoadSaveModal = function(rpcResponse, modal, folderCallback, openFile) {
531+
// clear the list
532+
modal.html('');
533+
534+
if (HuConApp.isResponseError(rpcResponse)) {
535+
return
536+
}
537+
538+
folderHtml = `
539+
<div onclick="HuConApp.{1}(\'{2}\')" class="item ok">
540+
<i class="folder icon"></i>
541+
<div class="content header">{3}</div>
542+
</div>
543+
`;
544+
545+
// Append the folder up if needed.
546+
if (HuConApp.Folder != '') {
547+
// Determine the parent folder
548+
var upperFolder = HuConApp.Folder.slice(0, HuConApp.Folder.lastIndexOf('/'));
549+
modal.append(HuConApp.formatVarString(folderHtml, folderCallback, upperFolder, '..'));
550+
}
551+
552+
// Add the folder to the empty list
553+
for (i=0; i<rpcResponse['result'].length; i++) {
554+
var filename = rpcResponse['result'][i];
555+
if (filename.indexOf('.') === -1) {
556+
var newFolder = HuConApp.Folder + '/' + filename;
557+
558+
// Do not show the examples folder as possible folder.
559+
if (!openFile && (newFolder == '/examples')) {
560+
continue;
561+
}
562+
563+
modal.append(HuConApp.formatVarString(folderHtml, folderCallback, newFolder, filename));
564+
}
565+
}
566+
567+
fileHtml = '';
568+
if (openFile) {
569+
fileHtml = `
570+
<div onclick="HuConApp.loadFileFromDevice(\'{1}\');" class="item ok">
571+
<i class="file icon"></i>
572+
<div class="content header">{1}</div>
573+
</div>
574+
`;
575+
} else {
576+
fileHtml = `
577+
<div onclick="$(\'#saveFilename\').val(\'{1}\');" class="item ok">
578+
<i class="file icon"></i>
579+
<div class="content header">{1}</div>
580+
</div>
581+
`;
582+
}
583+
584+
// Add the files to the empty list
585+
for (i=0; i<rpcResponse['result'].length; i++) {
586+
var filename = rpcResponse['result'][i];
587+
if (filename.substr(-HuConApp.FileExt.length) === HuConApp.FileExt) {
588+
modal.append(HuConApp.formatVarString(fileHtml, filename));
589+
}
590+
}
591+
}
592+
593+
HuConApp.formatVarString = function() {
594+
var args = [].slice.call(arguments);
595+
if(this.toString() != '[object Object]')
596+
{
597+
args.unshift(this.toString());
598+
}
599+
600+
var pattern = new RegExp('{([1-' + args.length + '])}','g');
601+
return String(args[0]).replace(pattern, function(match, index) { return args[index]; });
602+
}

webserver/templates/api.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<tr><td>get_version</td><td>Return the version of the server.</td></tr>
1313
<tr><td>poll</td><td>Return the last messages which come from the server or the running application.</td></tr>
1414
<tr><td>get_file_list</td><td>Return the list of files and folder.</td></tr>
15+
<tr><td>create_folder</td><td>Creates the folder.</td></tr>
1516
<tr><td>load_file</td><td>Return the content of the given file.</td></tr>
1617
<tr><td>save_file</td><td>Saves the given file on disk of the robot.</td></tr>
1718
<tr><td>is_running</td><td>Return True whenever a program is currently running.</td></tr>

0 commit comments

Comments
 (0)