How to restart an app programmatically? [Resolved]

Posted on
  • I have a problem of mostly my own making. My clock app writes various sensor settings to a log file and I have a python script that I can use to connect to the watch and download this log file (which is split by Bangle).

    However, deleting the files via the script doesn't quite have any effect on the files because my app I believe holds on to the file handle and keeps writing to the same file.
    To work around this, I put in a watch reboot using E.reboot() and this I believe clears the file handle and the files are actually gone for good and after reboot the app restarts and opens a new file handle.

    Now rebooting is a bazooka where a restart App would just do. If I could invoke a function on the app to tell it to reopen the file, that would be even more precise. Long-pressing BTN3 already does a reload, how can I mimic this via code?

    There is also the possibility I am using the StorageFile API incorrectly. There isn't a StorageFile.close() or Storage.close() so I didn't think I would need to close the file after each update and open it before each update.

    Any suggested approaches welcome. Thanks

    Python script for sync
    Custom BangleJS App

    Update: Reboot actually doesn't work. It leaves the watch storage in an inconsistent state and the app is unable to update the file anymore. Keeps throwing Uncaught Error: Unable to find or create file. Basically I have to now invoke Storage.eraseAll() and start fresh :(

    I'll have to think of a custom roll-over or something so the older log files are cleared out after it has been synced with a laptop.

  • Sun 2020.02.23

    'the possibility I am using the StorageFile API incorrectly'

    Were you aware @PiOfThings that it is possible to persue the source. Just R-Click on the arrow just to the Right of the heading

    https://banglejs.com/reference#l_Storage­File_read

    which launches:

    https://github.com/espruino/Espruino/blo­b/master/src/jswrap_storage.c#L427

    Descriptive text block there that might provide some insight

  • Hi - there's no 'close' because all the info is written to the file immediately. Closing it wouldn't actually do anything.

    However if you've started writing to a file and then you delete it and try and write again I can imagine that would cause issues. What I'd suggest is in your Python, when you go and clear the file, make sure you set currentFile = null; (you'd have to make it global - or maybe make a global function that does currentFile = null and erases the file?). That should allow everything to continue working without a reboot.

    ... but to answer your initial question, if you just want a 'soft' reboot, just call load(). On the cutting edge firmwares (and 2v05 and later when released) you can do load("filename") too.

  • Thanks @Gordon

    I never thought of putting the file handle in global area and manipulate it from there (me and my obsession with encapsulation). I shall do that and post the results here :-).

  • @PiOfThings, you still can go for an encapsulation... call it what ever WatchLog (singleton) or watchLog and let that be your object to talk to your python downloader... and in there, you hold on to the file handle... - I'd like to see the encapsulation obsession draw back... - m

  • Thanks @Gordon and @allObjects for the pointers. I did something hopefully in line with your suggestions and it seems to work! Wahooooo!

    Here's how I added to the global Bangle name space

    Bangle.AppLog = {
                currentFile: null,
                lock: false,
                init: (filename) => {
                    try{
                        if(filename != null && filename != ''){
                            Bangle.AppLog.currentFile = s.open(filename, "a");
                        }
                        else{
                            console.error("Log file name not provided");
                        }
                    }
                    catch(ex){
                        console.log("Failed to create file", ex);
                    }
                },
                write : (sentence) => {
                    if(Bangle.AppLog.lock != true){
                        Bangle.AppLog.currentFile.write(sentence­ + "\n");                    
                    }
                },
                clearLog: () => {
                    Bangle.AppLog.lock = true;
                    Bangle.AppLog.currentFile = null;
                    require("Storage").open("ftclog", "w").erase();
                    Bangle.AppLog.lock = false;
                    load();
                }
            };
    
    

    Calling Bangle.AppLog.clearlog() from the python script does the trick. All ftclog files get removed, the app gets reloaded and I am left with a grin on my face :D

    Also, as suggested by @Gordon I used the load() function in clearLog() and it works like a treat

    Thanks again.

    P.S. I should really save the incoming filename so I don't have to hardcode it in the clearLog() :D

  • That sounds great - and hopefully putting that code in Bangle.js means you can keep your python code nice and clean!

  • @PiOfThings, did you test the solution?

    Because 'it' - the lock, the clear, the unlock - is in the same "js event thread". I'm not really clear about what has changed to make it work, since while the .clearlog() is executed and nothing in it is asynchronous / deferred, nothing else is executed (no parallel processing / threads). Setting the .currentFile property controls the correct flow....

  • Haa you noticed... well, the write function is called by various Bangle events like GPS, HRM and step. These are asynchronous, I didn't want them to try to write to the file while I am trying to clear the log. It may loose a few readings, that's fine for now.

    The code does clear logs successfully without corrupting the file system, but now I have hit the next issue. If the file reaches max-size and fails to write, the file system gets corrupt yet again. Going to attempt a few strategies to mitigate :-). First up compression to enable more data :-)

  • These are asynchronous, I didn't want them to try to write to the file while I am trying to clear the log

    Actually that's one of the nice bits about JS. While code can be async, it all runs in the same thread - so if you do everything in one function (like you have done for clearLog) then you can be confident that nothing else will be running at the same time and causing you issues :)

    edit: Compression (if you use heatshrink) may cause you some issues - since it'll use char code 255 which isn't allowed in StorageFile... I'll just post some comments on http://forum.espruino.com/conversations/­344144/#comment15122336 where you mentioned the filesize

  • Oh... thanks for both the tips. I'll remove the redundant lock.

    @Gordon reference to your comment about the global AppLog namespace making python script easier, absolutely. I am planning to move the code for sync into another function in AppLog namespace. That way I'll have a little more control when I introduce compression and send back data uncompressed. I'll wait for your suggestions on compression.

  • @Gordon (in post# 10:)

    edit: Compression (if you use heatshrink) may cause you some issues - since it'll use char code 255 which isn't allowed in StorageFile... I'll just post some comments on

    OOOOPS... - so no binary data what so ever... Then I would suggest a slightly different management, since obviously 255 is used to detect unused space?

    I know that this is very space efficient, but I'd rather prefer - or have the option - 'wasting' some more bytes to have a way of knowing a (variable) record length. I could even think or having some time/performance optimizing information in the -kept alive RAM - that - if lost because of complete de-powering - can be recovered (with some kind of more expensive way like 'walking a chain' / following a block/record list where each of them knows its length and the last one in the list knows about to be the last one).

    Alternatives is to be able to have more than one storage and they can be of different type / behavior when it comes to manage size and space: require("Storage").n(<aNumber>).setup(<s­omeOptionObject\>) for setup and require("Storage").n(<aNumber>).... for use.

    Other alternative keeping single storage could define file extension(s) that indicate how the (allocated) space in/for the particular file is handled and, optional, leave it up to app specific/selectable space management function set/object to do the space management.

  • Yes, that's correct. Nothing stops you from compressing the data in big blocks and then writing those using the standard Storage.write function. The overhead of each file is currently 16 bytes, so not massive.

    You can even use Storage.write to allocate a large file and then write to it a bit at a time, so you could in fact just implement your own module for storing binary files.

    The current solution handled the vast majority of use-cases and seemed like a good balance of speed & sanity - but I can imagine that perhaps it could be modified to write one byte of length + X bytes of data into each block for every write command - and so avoid the limitation on 0xFF.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

How to restart an app programmatically? [Resolved]

Posted by Avatar for PiOfThings @PiOfThings

Actions