Question

Setup:

There are remote measurement stations, there is centralized collection/processing/presentation server (with a webserver) and there are observation stations which are to display the collected data for the customers.

These observation stations consist of bare bones simple embedded computer equipped with web browser working in kiosk mode displaying one specific webpage from the central server each. This webpage is updated with AJAX displaying latest measurements of given measurement station. Connected to a fixed monitor, these stations should run almost maintenance-free for years.

Now we've worked out most of the kinks but there's the matter: what if the webserver fails? The browser will load a "unreachable", "404", "Permission denied", "500", or whatever mode of failure the server took at that point, and remain there until someone manually reboots the observation station.

The general solution I came up with is to set the browser's home page not to the observed page, but to an always-available local HTML file which would perform periodic checks if the remote page was loaded and updates correctly, and reload it if it fails to perform for any reason.

Problem:

the problem lies in cross-frame-scripting. I guess the target webpage would have to load as a frame, iframe, object of type text/HTML, or some other way that will make it show up without removing/disabling the local "container" file. I wrote a cross-frame-scripting page a couple years ago, and circumventing the security counter-measures wasn't easy. Since then the security must have been tightened.

So, the page loaded from remote server contains a piece of javascript that is launched periodically (some setInterval) if everything went well, or doesn't if something was broken. Periodic arrival of this signal to the container frame makes it reset its timeout and not take any other action.

In case the signal does not arrive, as the timeout expires, the container begins periodically refreshing the loaded webpage, until the server is fixed and proper content is loaded, signalling that to the loader.

How do I get the remote page to signal "alive" (say, setting a variable) to the local (container) page loaded from a file:// URL each time a specific function is triggered?

Was it helpful?

Solution 2

The method for cross-frame, cross-site communication is using postMessage.

The contained frame, on each correct execution should perform:

window.top.postMessage('tyrp', '*');

The container document should contain:

window.onmessage = function(e)
{
    if (e.data == 'tyrp') {
        //reset timeout here
    }
};

OTHER TIPS

There is a library called porthole which basically does what SF.'s answer describes but in a more formal form. I just wrote a web page to switch showing one of two iframes. In the top level web page I have

var windowProxy;
windowProxy = new Porthole.WindowProxy(baseURL + '/porthole/proxy.html', frameId);
windowProxy.addEventListener(onMessage);
...
function onMessage(messageEvent) {
    if (messageEvent.origin !== baseURL) {
        $log.error(logPrefix + ': onMessage: invalid origin');
        console.dir(messageEvent);
        return;
    }
    if (messageEvent.data.pong) {
        pongReceived();
        return;
    }
    $log.log(logPrefix + ': onMessage: unknown message');
    console.dir(messageEvent);
}
...
var sendPing = function () {
    $log.log(logPrefix + ': ping to ' + baseURL);
    ...
    windowProxy.post({ 'ping': true });
};

plus some additional control logic. In the child web page the following is everything I had to add (plus a call to portholeService.init() from a controller):

// This service takes care of porthole (https://github.com/ternarylabs/porthole)
// communication if this is invoked from a parent frame having this web page
// as a child iframe. Usage of porthole is completely optional, and should
// have no impact on anything outside this service. The purpose of this
// service is to enable some failover service to be build on top of this
// using two iframes to switch between.
services.factory('portholeService', ['$rootScope', '$log', '$location', function ($rootScope, $log, $location) {
    $log.log('Hello from portholeService');

    function betterOffWithFailover() {
        ...
    }

    function onMessage(messageEvent) {
        $rootScope.$apply(function () {
            if (messageEvent.origin !== baseUrl) {
                $log.error('onMessage: invalid origin');
                console.dir(messageEvent);
                return;
            }

            if (!messageEvent.data.ping) {
                $log.error('unknown message');
                console.dir(messageEvent.data);
                return;
            }

            if (betterOffWithFailover()) {
                $log.log('not sending pong');
                return;
            }

            windowProxy.post({ 'pong': true });
        });
    }

    var windowProxy;
    var baseUrl;
    function init() {
        baseUrl = $location.protocol() + '://' + $location.host() + ':' + $location.port();
        windowProxy = new Porthole.WindowProxy(baseUrl + '/porthole/proxy.html');
        windowProxy.addEventListener(onMessage);
    }

    return {
        init: init
    };
}]);

For reference these pages are using AngularJS in case $rootScope.$apply etc was unfamiliar to you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top