Рольставни, реле-мини, const вместо var и SyntaxError: parse error (line 40) duk_js_compiler.c:3391

Здравствуйте. У меня по дому установили 7 роллет (или рольставен, не знаю, как правильно), которые управляются каждая двумя возвратными кнопками: нажимаешь одну, мотор крутит в одну сторону, нажимаешь другую - в другую. Две кнопки сразу не пробовал нажимать, в крайних положениях полотна есть концевые выключатели, так что можно давить сколько хочешь в разумных пределах. Они полностью открываются/закрываются N секунд, никакого датчика положения нет, но захотелось мне это автоматизировать.

К сожалению, у меня не совсем получилось: я по лени своей и неумению программировать создаю по одному виртуальному устройству и три правила для каждой из своих роллет, пробегая в цикле по объекту с настройками и статусами для каждой из них. Устройства создаются, но правила нормально работают только для последней роллеты, а предыдущие не работают. Такое впечатление, что программа статусы выставляет последней роллете, а не каждой соотвествующей. Что-то со скоупами переменных, объявленных внутри функций. Я, понятное дело, видимо читаю не ту документацию или не понимаю ее.

Я заменил var на const, и оно перестало компилироваться вообще с такой ошибкой:

SyntaxError: parse error (line 40) duk_js_compiler.c:3391

Не подскажете ли, как правильно делать? Я уверен у меня какая-то тупая ошибка или невнимательность, и мне будет стыдно, когда вы укажете на нее… Либо мне надо вначале каждой функции и колбека копировать “текущий девайс”, чтобы он не ссылался все время на последний, или вообще лучше по технологии “копи-пасте” задавать правила и девайсы…

Вот мой код (только вместо const у меня var):

var openMillis = 3 * 1000;

//var shutterPosition = new PersistentStorage("shutterPosition");

var shutters = {
    fl1_kitchen_shutter_left: {
        name: 'Left Kitchen Shutter',
        up: 'wb-mrm2-mini_92/K1',
        down: 'wb-mrm2-mini_92/K2',
        max: openMillis,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_kitchen_shutter_right: {
        name: 'Right Kitchen Shutter',
        up: 'wb-mrm2-mini_93/K1',
        down: 'wb-mrm2-mini_93/K2',
        max: openMillis,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_dining_shutter: {
        name: 'Dining Room Shutter',
        up: 'wb-mrm2-mini_220/K1',
        down: 'wb-mrm2-mini_220/K2',
        max: openMillis,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_living_shutter: {
        name: 'Living Room Shutter',
        up: 'wb-mrm2-mini_222/K1',
        down: 'wb-mrm2-mini_222/K2',
        max: openMillis,
        startedMovingUp: -1,
        startedMovingDown: -1
    }
};

for (const curDevice1 in shutters) {

    const curDevice = curDevice1;
    const curDeviceTarget = curDevice + '/targetPosition';
    const curDeviceActual = curDevice + '/position';
    const curRelayUp = shutters[curDevice].up;
    const curRelayDown = shutters[curDevice].down;

    defineVirtualDevice(curDevice, {
        title: shutters[curDevice].name,
        cells: {
            position: {
                title: "Position",
                type: "range",
                value: 0,
                max: shutters[curDevice].max,
                min: 0
            },
            targetPosition: {
                title: "Desired Position",
                type: "range",
                value: 0,
                max: shutters[curDevice].max,
                min: 0
            }
        }
    });

    defineRule(curDevice + '_target', {
        whenChanged: curDeviceTarget,
        then: function (newValue, devName, cellName) { // how to get the oldValue?
            if (!dev[curRelayUp] && !dev[curRelayDown] && dev[curDeviceTarget] !== dev[curDeviceActual]) {
                if (dev[curDeviceTarget] > dev[curDeviceActual]) {
                    dev[curRelayUp] = true;
                } else {
                    dev[curRelayDown] = true;
                }
            }
        }
    });


    defineRule(curDevice + '_up', {
        whenChanged: curRelayUp,
        then: function (newValue, devName, cellName) {
            if (newValue) { // started moving up
                if (dev[curRelayDown]) { // simultaneous press
                    dev[curRelayUp] = false; // switch off immediately
                    shutters[curDevice].startedMovingUp = -1; //we're not moving up
                    return;
                }

                if (shutters[curDevice].startedMovingUp > 0) { // already moving up!
                    return; //ignore
                }

                shutters[curDevice].startedMovingUp = Date.now();

                var targetDiff = dev[curDeviceTarget] - dev[curDeviceActual];
                if (targetDiff <= 0) {
                    // the command to move up didn't come from the target slider
                    // which means it comes from the physical buttons
                    targetDiff = shutters[curDevice].max - dev[curDeviceActual] + 1000;
                    // move to the end
                    // work for extra sec just in case
                }

                // never move longer then the max
                setTimeout(function () {
                    dev[curRelayUp] = false;
                }, targetDiff);



            } else { // stopped moving up
                if (shutters[curDevice].startedMovingUp < 0) {
                    // we werent moving up, ignore
                    return;
                }
                // record the position in the device
                var curPos = dev[curDeviceActual] +
                        (Date.now() - shutters[curDevice].startedMovingUp);
                shutters[curDevice].startedMovingUp = -1;

                // what if it's too high
                if (curPos > shutters[curDevice].max) {
                    curPos = shutters[curDevice].max;
                }

                dev[curDeviceActual] = curPos;
                dev[curDeviceTarget] = curPos;
            }
        }
    });

    defineRule(curDevice + '_down', {
        whenChanged: curRelayDown,
        then: function (newValue, devName, cellName) {
            if (newValue) { // started moving down
                if (dev[curRelayUp]) { // simultaneous press
                    dev[curRelayDown] = false; // switch off immediately
                    shutters[curDevice].startedMovingDown = -1; //we're not moving down
                    return;
                }

                if (shutters[curDevice].startedMovingDown > 0) { // already moving down!
                    return; //ignore
                }

                shutters[curDevice].startedMovingDown = Date.now();

                var targetDiff = dev[curDeviceActual] - dev[curDeviceTarget];
                if (targetDiff <= 0) {
                    // the command to move down didn't come from the target slider
                    // which means it comes from the physical buttons
                    targetDiff = dev[curDeviceActual] + 1000;
                    // move to the end
                    // work for extra sec just in case
                }

                // never move longer then the max
                setTimeout(function () {
                    dev[curRelayDown] = false;
                }, targetDiff);



            } else { // stopped moving up
                if (shutters[curDevice].startedMovingDown < 0) {
                    // we werent moving down, ignore
                    return;
                }
                // record the position in the device
                var curPos = dev[curDeviceActual] - // note the minus here as we were moving down
                        (Date.now() - shutters[curDevice].startedMovingDown);
                shutters[curDevice].startedMovingDown = -1;

                // what if it's too low
                if (curPos < 0) {
                    curPos = 0;
                }

                dev[curDeviceActual] = curPos;
                dev[curDeviceTarget] = curPos;
            }
        }
    });

}

Добрый день.
При срабатывании then-функций curDeviceTarget, curRelayUp, curRelayDown будут равны значению из последней итерации цикла.
Вам необходимо обернуть определение правил в функции и вынести их из цикла. А в цикле вызывать данные функции и через аргументы передавать значения переменных.
И необходимо использовать var, а не const.

Спасибо, получилось. А как в целом такой способ управления? Может, я изобрел велосипед? И надо ли мне пользоваться PersistentStorage или мои виртуальные устройства сами запомнят положение ставен между перезагрузками? (Я, конечно, проверю, но это первое мое правило, которое я написал, и мне не хватает совета бывалых)

ps/ вроде положение рольставен сохраняется после перезагрузки. хорошие я правила написал! =)

1 лайк

финальный вариант

в новой версии - слайдер показывает в процентах от 0 до 100, и для каждой роллеты нужно установить время открытия и закрытия, на случай, если мотор крутит с разной скоростью

// посчитайте для каждой роллеты реальное время, за которое они полностью открываются
// и закрываются, и установите в поля timeUp и timeDown
var openMillis = 15 * 1000;  // 15секунд, например
// это время, которое надо дополнительно крутить вниз или вверх, чтобы роллеты точно
// закрылись. должны быть корректно настроены концевые выключатели, чтобы не мучить мотор
var extraTime = 400;

var shutters = {
    fl1_kitchen_shutter_left: {
        name: 'Left Kitchen Shutter',
        up: 'wb-mrm2-mini_92/K1',
        down: 'wb-mrm2-mini_92/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_kitchen_shutter_right: {
        name: 'Right Kitchen Shutter',
        up: 'wb-mrm2-mini_93/K1',
        down: 'wb-mrm2-mini_93/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_dining_shutter: {
        name: 'Dining Room Shutter',
        up: 'wb-mrm2-mini_220/K1',
        down: 'wb-mrm2-mini_220/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_living_shutter: {
        name: 'Living Room Shutter',
        up: 'wb-mrm2-mini_222/K1',
        down: 'wb-mrm2-mini_222/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_office_shutter: {
        name: 'Office Shutter',
        up: 'wb-mrm2-mini_149/K1',
        down: 'wb-mrm2-mini_149/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_boiler_shutter: {
        name: 'Boiler Room Shutter',
        up: 'wb-mrm2-mini_113/K1',
        down: 'wb-mrm2-mini_113/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    },
    fl1_storage_shutter: {
        name: 'Storage Room Shutter',
        up: 'wb-mrm2-mini_115/K1',
        down: 'wb-mrm2-mini_115/K2',
        timeUp: openMillis,
        timeDown: openMillis,
        maxUnits: 100,
        startedMovingUp: -1,
        startedMovingDown: -1
    }
};

for (var curDevice in shutters) {
    // calculate how many millis in one unit
    shutters[curDevice].unitUp = shutters[curDevice].timeUp / shutters[curDevice].maxUnits;
    shutters[curDevice].unitDown = shutters[curDevice].timeDown / shutters[curDevice].maxUnits;
    // calculate how many units before start or end to stick
    shutters[curDevice].unitStickUp = extraTime / shutters[curDevice].unitUp;
    shutters[curDevice].unitStickDown = extraTime / shutters[curDevice].unitDown;
}

var targetRule = function (deviceTarget, deviceActual, relayUp, relayDown) {
    return {
        whenChanged: deviceTarget,
        then: function () {
            if (!dev[relayUp] && !dev[relayDown] && dev[deviceTarget] !== dev[deviceActual]) {
                if (dev[deviceTarget] > dev[deviceActual]) {
                    dev[relayUp] = true;
                } else {
                    dev[relayDown] = true;
                }
            }
        }
    };
};

var upRule = function (device, deviceTarget, deviceActual, relayUp, relayDown) {
    return {
        whenChanged: relayUp,
        then: function (newValue) {
            if (newValue) { // started moving up
                if (dev[relayDown]) { // simultaneous press
                    dev[relayUp] = false; // switch off immediately
                    shutters[device].startedMovingUp = -1; //we're not moving up
                    return;
                }

                if (shutters[device].startedMovingUp > 0) { // already moving up!
                    return; //ignore
                }

                shutters[device].startedMovingUp = Date.now();

                var targetDiff = dev[deviceTarget] - dev[deviceActual]; // in units
                if (targetDiff <= 0 || shutters[device].maxUnits - dev[deviceTarget] < shutters[device].unitStickUp) {
                    // the command to move up didn't come from the target slider
                    // which means it comes from the physical buttons
                    // OR the target is very close to the end
                    targetDiff = shutters[device].maxUnits - dev[deviceActual] + shutters[device].unitStickUp;
                    // move to the end
                    // work for extra sec just in case
                }

                // never move longer then the max
                setTimeout(function () {
                    dev[relayUp] = false;
                }, targetDiff * shutters[device].unitUp);



            } else { // stopped moving up
                if (shutters[device].startedMovingUp < 0) {
                    // we werent moving up, ignore
                    return;
                }
                // record the position in the device
                var curPos = dev[deviceActual] +
                        ((Date.now() - shutters[device].startedMovingUp) / shutters[device].unitUp);
                shutters[device].startedMovingUp = -1;

                // what if it's too high
                if (curPos > shutters[device].maxUnits) {
                    curPos = shutters[device].maxUnits;
                } else {
                    curPos = Math.round(curPos);
                }

                dev[deviceActual] = curPos;
                dev[deviceTarget] = curPos;
            }
        }
    };
};

var downRule = function (device, deviceTarget, deviceActual, relayUp, relayDown) {
    return {
        whenChanged: relayDown,
        then: function (newValue) {
            if (newValue) { // started moving down
                if (dev[relayUp]) { // simultaneous press
                    dev[relayDown] = false; // switch off immediately
                    shutters[device].startedMovingDown = -1; //we're not moving down
                    return;
                }

                if (shutters[device].startedMovingDown > 0) { // already moving down!
                    return; //ignore
                }

                shutters[device].startedMovingDown = Date.now();

                var targetDiff = dev[deviceActual] - dev[deviceTarget];
                if (targetDiff <= 0 || dev[deviceTarget] < shutters[device].unitStickDown) {
                    // the command to move down didn't come from the target slider
                    // which means it comes from the physical buttons
                    // OR the target is very close to the end
                    targetDiff = dev[deviceActual] + shutters[device].unitStickDown;
                    // move to the end
                    // work for extra sec just in case
                }

                // never move longer then the max
                setTimeout(function () {
                    dev[relayDown] = false;
                }, targetDiff * shutters[device].unitDown);



            } else { // stopped moving down
                if (shutters[device].startedMovingDown < 0) {
                    // we werent moving down, ignore
                    return;
                }
                // record the position in the device
                var curPos = dev[deviceActual] - // note the minus here as we're moving down
                        ((Date.now() - shutters[device].startedMovingDown) / shutters[device].unitDown);
                shutters[device].startedMovingDown = -1;

                // what if it's too low
                if (curPos < 0) {
                    curPos = 0;
                } else {
                    curPos = Math.round(curPos);
                }

                dev[deviceActual] = curPos;
                dev[deviceTarget] = curPos;
            }
        }
    };
};

for (var curDevice in shutters) {

    var curDeviceTarget = curDevice + '/targetPosition';
    var curDeviceActual = curDevice + '/position';
    var curRelayUp = shutters[curDevice].up;
    var curRelayDown = shutters[curDevice].down;

    defineVirtualDevice(curDevice, {
        title: shutters[curDevice].name,
        cells: {
            position: {
                title: "Position",
                type: "range",
                value: 0,
                max: shutters[curDevice].maxUnits,
                min: 0,
                readonly: true
            },
            targetPosition: {
                title: "Desired Position",
                type: "range",
                value: 0,
                max: shutters[curDevice].maxUnits,
                min: 0
            }
        }
    });

    defineRule(curDevice + '_target', targetRule(curDeviceTarget, curDeviceActual, curRelayUp, curRelayDown));

    defineRule(curDevice + '_up', upRule(curDevice, curDeviceTarget, curDeviceActual, curRelayUp, curRelayDown));

    defineRule(curDevice + '_down', downRule(curDevice, curDeviceTarget, curDeviceActual, curRelayUp, curRelayDown));

}
1 лайк