Design Pattern: Gate Keeper

So then the code you posted above is incorrect. It tests for newPressCount == 1. When it’s 2 or more the Timer will be created again.

No, you wouldn’t want to release the lock inside the timer’s lambda. You are only trying to prevent more than one Timer being created. So you’d do something like:

try {
    myLock.lock
    if(recall1Timer != null){
        recall1Timer = createTimer(now.plusMillis(600), [ |
            // timer code
            recall1Timer = null
        ])
    }
}
catch Exception {
    // log out the error
}
finally {
    myLock.unlock
}

The only part of the code that is locked is the check whether the timer needs to be created or not and the creation of the Timer. So the first time the rule runs it acquires the lock, sees the timer doesn’t exist so it creates it. The second time the rule runs it has to wait for the lock. Once it acquires it it sees that the timer already exists so it doesn’t do anything.

Note that this is somewhat dangerous as there are some types of errors that can occur inside the try that do not rise up to the rule but can cause the rule to exit, leaving your lock locked forever. For this reason I think the AtomicInteger approach is a better approach over all. You just need to check for >=, not ==.

I’m not sure I understand your reasoning: 2 == 1 should return false, shouldn’t it? Therefore only the event incrementing atomicRecall1PressCount from 0 to 1 will be the one that creates the timer. All the other events of the current series will produce values higher than 1. Only after the timer and its subsequent cooling down timer have finished atomicRecall1PressCount will be reset to 0 and the next series of events can start.

Please excuse me should I have misunderstood your answer or a basic concept of the rules DSL. (I have flooded my code with logging statements and the output is consistent with my explanation above: I never see an extra set of timers being created during one series of events.)

Yes, of course. I hadn’t thought of that. In my attempts I was holding the lock before the creation of the timer and only unlocked it after the lambda’s execution.That always led to exceptions. Thanks for explaining that to me.

You’re right. Not sure what I was thinking.

Hello everybody,
I tried to use the Gatekeeper DP for my 433-blinds and thought I’d give python a try so that the gatekeeper could be used from any rule.
I’m struggeling to make it run by a (probably) simple problem (but i really searched anywhere: docs, examples, I tried any combination): Could somebody be so kind and give me a hint how (and where) to define the logger that is needed for the gatekeeper?
The version is oh3.1 and the patched library-helper is installed

The log-error is: SyntaxError: no viable alternative at input ‘Gatekeeper’ in /etc/openhab/automation/jsr223/python/personal/Rollo2.py

My code is basically the original example

from core.rules import rule
from core.triggers import when
from org.slf4j import LoggerFactory
from community.gatekeeper import Gatekeeper

gk = None
rollolog = LoggerFactory.getLogger("org.openhab.core.automation.Rollo2")
@rule("Rollo2")
@when("Time cron cron 0 0 8 * * ?")
@when("Item RolloProxy2OG received command DOWN")
def rollo2og(event):
    if not gk:
        global gk
        gk = new Gatekeeper(rollolog)
    else:
        gk.cancel_all()
    rollo2og.logInfo("start rollo2")
    gk.add_command("2s", lambda: events.sendCommand("RolloSchlaf1", "DOWN"))
    gk.add_command("2s", lambda: events.sendCommand("ROLLOschlaf2", "DOWN"))

Many Regards and thanks for everybody who helped me with their examples, tutorials…
Camo

A logger is automatically created for you when you use @rule. The logger is attached to the rule’s function. You don’t have to define the logger yourself. See Logging — openHAB Helper Libraries documentation

Hello All,

runtimeInfo:
  version: 3.1.0
  buildString: Release Build
locale: default
systemInfo:
  configFolder: /etc/openhab
  userdataFolder: /var/lib/openhab
  logFolder: /var/log/openhab
  javaVersion: 11.0.12
  javaVendor: Azul Systems, Inc.
  javaVendorVersion: Zulu11.50+19-CA
  osName: Linux
  osVersion: 5.10.60-sunxi
  osArchitecture: arm
  availableProcessors: 4
  freeMemory: 68282840
  totalMemory: 253845504
bindings:
  - serial

  userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
    like Gecko) Chrome/94.0.4606.61 Safari/537.36
timestamp: 2021-09-24T08:15:43.999Z

ı want to use this script for my group commands,

https://community.openhab.org/t/design-pattern-gate-keeper/36483#oh-3x-javascript-6

but I couldn’t figure out how to install and prepare this tool.
i read this article,

https://github.com/rkoshak/openhab-rules-tools
and this
https://github.com/rkoshak/openhab-rules-tools/tree/main/gatekeeper

and this
https://www.openhab.org/docs/configuration/jsr223.html#script-locations

I can’t find the automation folder’s location in OH3.

Can you explain in detail how to install and use the libraries on OH3?

I’m new to the Openhab ecosystem, I don’t know about scripts and libraries. I’ve been reading many topics and manuals, but I haven’t been able to put the platform in my head yet.

-Which method is faster
-Which method uses fewer resources,
-which method is stable and trouble-free.

I am an embedded system designer and code developer.
I am trying to learn which one is closer to me or more efficient.

Thank you for your time

It might not be created automatically. You can create it by hand though. On Linux

sudo -u openhab mkdir /etc/openhab/automation

Or when you follow the instructions to copy the library to your config, the needed folders will be created for you. Just cp -r openhab-rules-tools/gatekeeper/python/automation /etc/openhab.

Note that gatekeeper depends on time_utils so you need to copy that over too.

So the steps are:

  1. clone openhab-rules-tools
  2. cp -r openhab-rules-tools/time_utils/javascript/automation /etc/openhab
  3. cp -r openhab-rules-tools/gatekeeper/javascript/automation /etc/openhab

Done, the libraries are now installed.

The example in the readme for gatekeeper should show how to use it. At a high level, import the library, instantiate a Gatekeeper Object and use it. The test code is often helpful for examples as well.

If you run into trouble let us know.

Faster in what way? What methods are you really referring to? Between Scripts and Libraries? If so the question doesn’t make sense. Scripts are what are run in response to events. These are basically synonymous withe “rules”. Libraries, more properly called modules, don’t run at all on their their own. They must be loaded and used by a Script.

But in a home automation context, faster almost never matters. It’s far far more important to worry about legibility, simplicity, and maintainability than it is that your rules run fast. Even a really poorly run rule will run to completion in <100 msec which is more than fast enough for users to feel that then system is responsive (e.g. flipping the switch turns on the light).

Again, if it’s between scripts and libraries the question is nonsense. And again, in a home automation context this just doesn’t matter. Even when running on an RPi 2 or 3 you have ample CPU and RAM to be incredibly wasteful and have zero impact on the performance of your automations. So again, it’s far more important to make your code simple and maintainable than it is to worry about the resources it consumes.

Again, if it’s between scripts and libraries the question doesn’t make sense. However, if the question is modified to ask which rules language and which way of writing rules (text files or the UI) is more stable than the answer is neither. They are both pretty much the same in terms of stability. Which is more trouble-free? That’s hard to say. Right now I’d lean towards UI rules being more trouble-free but that was because of some bugs and such with text based rules that have mostly been fixed.

The biggest things I can suggest are:

Thank you, I will do.

I mean,
-DSL
-JS
-Python
comparing

thank you, your explain was quite enough and enlightening for me.

Hello Rick,
After a long break, I was able to return to automation and Openhab.

I have been trying to understand and understand the subject of Gatekeeper for 3 days, but I’m confused!

I want to put a delay between the commands for collective group commands, like those made for 433 MHz.
Openhab sends too many commands in collective commands very quickly and in a series. I have to make analyzes in the meantime and I need to make transformations between data types. The MCU on the hardware side (communicates with Openhab via Serial Bridge) is insufficient.
As a result, I have to save time for MCU and I need to put delays between the commands.

“Openhab-Rules-Tools” came directly from Openhabian.
But I couldn’t see/find an example or template made with JS for Gatekeeper.
I could not solve the problem by looking at the source file.

As a result, I did not understand how to use the “Openhab-Rules-Tools” library.

Will I write a new script using the library?
Should he come under “settings/scripts/”? (I don’t have a template either)
and
Under “Settings/Addons/Automation” I could not find a template for Gatekeeper and so I couldn’t add it.

If you help, I would like to open a title that can be used as a reference for people who are new and do not have knowledge/experience, and make this title a step-by-step guide for Gatekeeper.

My system is as follows:

Runtimeınfo:
   Version: 3.3.0
   Buildstring: Release Build
Locale: TR-TR
Systemınfo:
   Configfolder: /Etc /Openhab
   Userdatafolder:/VAR/Lib/Openhab
   logfolder:/var/log/openhab
   Javaversion: 11.0.16
   Javavendor: Ubuntu
   Osname: Linux
   Osversion: 5.15.48-Sunxi
   OSARCHitecture: ARM
   AvailaLebleprocessors: 4
   Freememory: 32443112
   Totalmemory: 194904064
   Startlevel: 100
Bindings:
   - denonmarantz
   - LIRC
   - Serial
   - Telegram
.
.
...
Timestamp: 2022-12-06T10: 23: 37.286z

Yes. It’s a library that you import into your scripts/rules. It’s standard JavaScript so you import it in the standard JavaScript way.

No, libraries do not appear in the UI anywhere. Only on the file system.

You might do well to go through a few JavaScript tutorials because for the most part, the OH docs will not cover the basics like how to import a library in depth. Importing an using a library is one of those basic JavaScript things.

I do notice that this is one of the several design pattern tutorials I need to update for JS Scripting but the usage example in the OP is still useful.

The first part (find and install) was done for you by openHABian so we can skip to the usage example.

If you break down the usage example, the first two lines import the library, third line creates the GateKeeper and the last two lines show adding a command to run with a 1 second delay following it and cancelling all scheduled commands.

If you look at the JS Scripting add-on’s docs it says

Scripts may include standard NPM based libraries by using CommonJS require . The library search will look in the path automation/js/node_modules in the user configuration directory.

Searching further down the page reveals a few examples of require but if you need more to understand it, there are many tutorials on the internet (e.g. How to use require() in ECMAScript modules - DEV Community).

Additionally, the openhab_rules_tools readme states:

Be sure to look at the comments, the code, and the tests for further details on usage and details.

A perfect example can be found in the Gatekeeper tests for how to import exactly the Gatekeeper itself. From that we find the require statement that imports Gatekeeper.

var { gatekeeper } = require('openhab_rules_tools');

Since you are running a version of OH prior to this past week, you’ll have to use the cache to save your Gatekeeper between runs of the rule, if you are creating rules in the UI.

var gk = cache.get(ruleUID+'_gk', () => new gatekeeper.Gatekeeper());

In any version of OH past 3.4 M5 you’ll use the privateCache or sharedCache which I believe will look something like this:

var { privateCache } = require('@runtime');

var gk = privateCache.get(ruleUID+'_gk', () => new gatekeeper.Gatekeeper());

Then you just use it. The first argument to addCommand is the amount of time to wait (supports anything supported by time.toZDT(), see JavaScript Scripting - Automation | openHAB) after executing the passed in command before executing the next one .

You may need to install the latest openhab-js library for openhab_rules_tools to work on an version of OH that old though. You can do that from openhabian_config.

1 Like

Hi All,
I am trying the Gatekeeper Script and test example for a week but I am facing the below error, again by again.
my system configuration is this.

runtimeInfo:
  version: 3.4.0.M6
  buildString: Milestone Build
locale: tr-TR
systemInfo:
  configFolder: /etc/openhab
  userdataFolder: /var/lib/openhab
  logFolder: /var/log/openhab
  javaVersion: 11.0.17
  javaVendor: Ubuntu
  osName: Linux
  osVersion: 5.15.80-sunxi
  osArchitecture: arm
  availableProcessors: 4
  freeMemory: 113579352
  totalMemory: 258334720
  startLevel: 100
bindings:
  - denonmarantz
  - lirc
  - serial
  - telegram
clientInfo:
  device:
    ios: false
    android: false
    androidChrome: false
    desktop: true
    iphone: false
    ipod: false
    ipad: false
    edge: false
    ie: false
    firefox: false
    macos: false
    windows: true
    cordova: false
    phonegap: false
    electron: false
    nwjs: false
    webView: false
    webview: false
    standalone: false
    os: windows
    pixelRatio: 1
    prefersColorScheme: light
  isSecureContext: false
  locationbarVisible: true
  menubarVisible: true
  navigator:
    cookieEnabled: true
    deviceMemory: N/A
    hardwareConcurrency: 8
    language: en-US
    languages:
      - en-US
      - en
      - tr
    onLine: true
    platform: Win32
  screen:
    width: 1920
    height: 1080
    colorDepth: 24
  support:
    touch: false
    pointerEvents: true
    observer: true
    passiveListener: true
    gestures: false
    intersectionObserver: true
  themeOptions:
    dark: dark
    filled: true
    pageTransitionAnimation: default
    bars: light
    homeNavbar: default
    homeBackground: default
    expandableCardAnimation: default
  userAgent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML,
    like Gecko) Chrome/108.0.0.0 Safari/537.36
timestamp: 2022-12-25T20:30:22.184

and the Gatekeeper test code is;

var { time } = require('openhab');
var { gatekeeper } = require('openhab_rules_tools');
var logger = log('rules_tools.Gatekeeper Tests');

var testFunGen = (test, num) => {
  logger.debug('generating function for {} run{}', test, num);
  return () => {
    logger.debug('{}: Test {} ran', test, num);
    cache.put(test + '_run' + num, time.toZDT());
  };
}

logger.info('Starting Gatekeeper tests');

var reset = (test) => {
  logger.debug('resetting {}');
  cache.put(test, null);
  logger.debug('resetting {}', test + '_start');
  cache.put(test + '_start', null);
  for (var i = 1; i <= 4; i++) {
    logger.debug('resetting {}', test + '_run' + 1)
    cache.put(test + '_run' + i, null);
  }
}

// Test 1: Scheduling
var gk1 = new gatekeeper.Gatekeeper();
var TEST1 = ruleUID + '_test1';
reset(TEST1);
cache.put(TEST1 + '_start', time.ZonedDateTime.now());
gk1.addCommand('PT1s', testFunGen(TEST1, 1));
gk1.addCommand('PT2s', testFunGen(TEST1, 2));
gk1.addCommand('PT3s', testFunGen(TEST1, 3));
gk1.addCommand(500, testFunGen(TEST1, 4));

actions.ScriptExecution.createTimer(time.toZDT('PT6.51s'), () => {
  var success = true;
  const start = cache.get(TEST1 + '_start');
  const run1 = cache.get(TEST1 + '_run1');
  const run2 = cache.get(TEST1 + '_run2');
  const run3 = cache.get(TEST1 + '_run3');
  const run4 = cache.get(TEST1 + '_run4');
  if (start === null) {
    logger.error('{} Failed to get starting timestamp', TEST1);
    success = false;
  }
  if (success && run1 === null) {
    logger.error('{} run1 failed to run!', TEST1);
    success = false;
  }
  if (success && run2 === null) {
    logger.error('{} run2 failed to run!', TEST1);
    success = false;
  }
  if (success && run3 === null) {
    logger.error('{} run3 failed to run!', TEST1);
    success = false;
  }
  if (success && run4 === null) {
    logger.error('{} run4 failed to run!', TEST1);
    success = false;
  }

  if (success) {
    logger.info('\n{}\n{}\n{}\n{}\n{}', start.toString(), run1.toString(), run2.toString(), run3.toString(), run4.toString());
    const dur1 = time.Duration.between(run1, run2).seconds();
    const dur2 = time.Duration.between(run2, run3).seconds();
    const dur3 = time.Duration.between(run3, run4).seconds();

    if (start.isAfter(run1)) {
      logger.error('{} failed, run1 ran before start!', TEST1);
      success = false;
    }
    if (success && dur1 != 1) {
      logger.error('{} failed, time between run1 and run2 is {} seconds.', dur1);
      success = false;
    }
    if (success && dur2 != 2) {
      logger.error('{} failed, time between run2 and run3 is {} seconds', dur2);
      success = false;
    }
    if (success && dur3 != 3) {
      logger.error('{} failed, time between run3 and run4 is {} seconds', dur3);
    }
    if (success) {
      logger.info('Gatekeeper test 1 success!');
    }
  }
});

// Test 2: cancelAll
var gk2 = new gatekeeper.Gatekeeper();
var TEST2 = ruleUID + '_test2'
reset(TEST2);
gk2.addCommand('PT1.5s', testFunGen(TEST2, 1));
gk2.addCommand('PT2s', testFunGen(TEST2, 2));
gk2.addCommand('PT3s', testFunGen(TEST2, 3));
gk2.addCommand(500, testFunGen(TEST2, 4));

actions.ScriptExecution.createTimer(time.ZonedDateTime.now().plus(2750, time.ChronoUnit.MILLIS), () => {
  var success = true;
  const run1 = cache.get(TEST2 + '_run1');
  const run2 = cache.get(TEST2 + '_run2');
  const run3 = cache.get(TEST2 + '_run3');
  const run4 = cache.get(TEST2 + '_run4');

  if (!run1) {
    logger.error('{} failed, run1 did not run', TEST2);
    success = false;
  }
  if (success && !run2) {
    logger.error('{} failed, run2 did not run', TEST2);
    success = false;
  }
  if (success && run3) {
    logger.error('{} failed, run3 ran too soon', TEST2);
    success = false;
  }
  if (success && run4) {
    logger.error('{} failed, run4 ran too soon', TEST2);
    success = false;
  }
  if (success) {
    gk2.cancelAll();
    actions.ScriptExecution.createTimer(time.toZDT('PT4s'), () => {
      var success = true;
      const run3 = cache.get(TEST2 + '_run3');
      const run4 = cache.get(TEST2 + '_run4');

      if (run3) {
        logger.error('{} failed, run3 ran after being cancelled');
        success = false;
      }
      if (success && run4) {
        logger.error('{} failed, run4 ran after being cancelled');
      }
      if (success) {
        logger.info('Gatekeeper test 2 success!')
      }
    });
  }
});

and the error is;

2022-12-21 00:26:03.560 [ERROR] [rg.apache.cxf.jaxrs.utils.JAXRSUtils] - No message body reader has been found for class java.util.Map, ContentType: application/octet-stream
2022-12-21 00:26:03.617 [WARN ] [s.impl.WebApplicationExceptionMapper] - javax.ws.rs.WebApplicationException: HTTP 415 Unsupported Media Type
	at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1473)
	at org.apache.cxf.jaxrs.utils.JAXRSUtils.processRequestBodyParameter(JAXRSUtils.java:950)
	at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:881)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:215)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:79)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265)
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:550)
	at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:74)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440)
	at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:294)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:90)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
	at org.eclipse.jetty.server.Server.handle(Server.java:516)
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
	at java.base/java.lang.Thread.run(Thread.java:829)

Do you have any suggestions or help?

Thank you…

Please upgrade to the 3.4.0 release!

Hi Florian,

already upgraded it.

Thank you BK.

You are on the Milestone 6.
This is older than the 3.4.0 Release.

If you are on the release, the build string would say something like Release Build.

1 Like

In addition to what was suggested, you don’t have the JS Scripting add-on installed.

openhab_rules_tools does not work in Nashorn. In fact, require doesn’t exist as a special word in Nashorn.

But I have to say I’ve never seen an error anything like that so if it persists after upgrading to the 3.4 release and installed the JS Scripting add-on and verifying that openhab_rules_tools is also installed come back and we can explore.

Hi Rich,
Sorry, it is my mistake, I forget to reply to this issue.

it was the solution to the problem below.

No problem at the moment.


I am currently facing some issues, but I would like to at least try to solve them myself first.
If I can’t do/solve those, I will inform to group.

Sorry again and thank you.

1 Like

Hello! Just a quick question, because I can’t get this to work in OH4: does it still work the same way?

In my .rules file I am defining a queue and a timer, but it seems they can’t be accessed in a single rule in the very same file. The log tells me: The field Tmp_gctl-controllerRules.queue refers to the missing type Object.

Additionally, in the WebUI, the rule can be viewed (obviously not edited, because I created it using a textual config file). There, the declaration of the queue and the timer is gone. What am I doing wrong?

What I want to achieve: I want to queue up commands (which the controller receives from other rules) and execute them one-by-one, only starting the next one when the previous finished.

The rule file in question:

import java.util.concurrent.ConcurrentLinkedQueue

val Queue<String> queue = new ConcurrentLinkedQueue()
var Timer timer = null

rule VolTargetController
when
    Item gctl_voltarget_controller received command
then
    queue.add(receivedCommand.toString)

    if(timer === null) {
        timer = createTimer(now, [ |
            if(queue.peek !== null) {
                if (gctl_voltarget_stat.state == CLOSED) {  // condition is met if the previous command finished             
                    val command = queue.poll

                    // just logging for degubbing purposes, instead executing the command
                    logInfo("VolTargetController","next cmd: " + command)
                }                 
                timer.reschedule(now.plusMillis(1000))
            }            
        ])
    }
end

I don’t know. I dropped Rules DSL years ago and have not kept all these old Rules DSL examples up to date. I probably should work the same but :person_shrugging: .

I know there have been changes in how Rules DSL rules are executed since OH 3 which could be a problem. Each rule gets it’s own thread but all the timers created in that rule also share a thread. So it might be possible that there might be a collision between the timer and the rule. I don’t know what would happen in that case.

You have to import Queue too.

For the latest maintained implementation of this see openHAB Rules Tools Announcements which provides a library implementation of most of the design patterns.