Skip to content

rgehrsitz/rex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

markdown Copy code

REX - Rules Engine eXtended

License Auto Wiki codecov

REX is a rules engine designed to process complex conditions and actions using a structured JSON format for rule definitions. It allows for defining rules, conditions, and actions that are compiled into bytecode by the REX Compiler, then executed by the REX Engine. REX is designed to be used in conjunction with a key/value store such as Redis or NATS, where REX subscribes to and receives updates from the key/value store, evaluates the updated value, then updates and publishes applicable results back to the store.

Features

  • Define rules using JSON
  • Support for various data types and comparison operations
  • Logical and control flow instructions
  • Action execution based on rules
  • New: Scripting capabilities using Otto (JavaScript engine)

Getting Started

Prerequisites

  • Go 1.20 or higher
  • Redis server running on localhost:6379 (for default configuration)

Installation

Clone the repository:

git clone https://github.com/rgehrsitz/rex.git

Navigate to the project directory:

cd rex

Running the Executables

The REX repository includes four main executables: rexc, rexd, redis_setup, and rule_gen. Below are the details on how to build, run, and understand the purpose of each executable.

1. Compiler (rexc)

Purpose: The rexc executable is the compiler that translates rules defined in JSON format into bytecode instructions that the runtime engine can execute.

How to Build:

go build ./cmd/rexc

How to Run:

./rexc -rules <path_to_rules.json> [-loglevel <level>] [-logoutput <output>]

Command-line options:

  • -rules: (Required) Path to the input JSON file containing the rules.
  • -loglevel: (Optional) Set log level. Valid values are panic, fatal, error, warn, info, debug, trace. Default is "info".
  • -logoutput: (Optional) Set log output. Valid values are console or file. Default is "console".

Example:

./rexc -rules examples/rules2.json -loglevel debug -logoutput file

2. Runtime Engine (rexd)

Purpose: The rexd executable is the runtime engine that processes the bytecode generated by rexc. It uses a Redis store for managing and updating facts in real-time based on the rules.

How to Build:

go build ./cmd/rexd

How to Run:

./rexd -config <path_to_config.json>

Command-line options:

  • -config: (Optional) Path to the configuration file. If not specified, rexd will look for a file named rex_config.json in the current directory, $HOME/.rex, and /etc/rex.

Configuration File (rex_config.json): The configuration file is in JSON format and supports the following options:

{
  "bytecode_file": "output.bytecode",
  "logging": {
    "level": "debug",
    "destination": "console",
    "timeFormat": "unixnano"
  },
  "redis": {
    "address": "localhost:6379",
    "password": "",
    "database": 0,
    "channels": ["weather", "system", "network", "energy", "water"]
  },
  "engine": {
    "priority_threshold": 1
  }
}

Example:

./rexd -config cmd/rexd/rex_config.json

3. Redis Setup (redis_setup)

Purpose: The redis_setup executable initializes the Redis database with default values necessary for some testing of the REX system. It also provides a CLI for modifying values during debugging.

How to Build:

go build ./tools/redis_setup

How to Run:

./redis_setup

This tool doesn't have any command-line options. It connects to Redis at localhost:6379 by default.

After running, it provides an interactive CLI with the following command:

set <group:key> <value>

Example:

./redis_setup
Enter command (set <group:key> <value> or exit): set weather:temperature 30.5

4. Rule Generator (rule_gen)

Purpose: The rule_gen executable generates a large number of random rules in JSON format, which can be used for testing and benchmarking the REX system.

How to Build:

go build ./tools/rule_gen

How to Run:

./rule_gen [-rules <number_of_rules>] [-output <output_file.json>]

Command-line options:

  • -rules: (Optional) Number of rules to generate. Default is 1000.
  • -output: (Optional) Output file name. Default is "generated_ruleset.json".

Example:

./rule_gen -rules 1000 -output generated_ruleset.json

Usage

Workflow

  1. Define your rules in a JSON file.
  2. Use rexc to compile the rules into bytecode.
  3. Set up your Redis instance and initialize it with redis_setup if needed.
  4. Run rexd with the compiled bytecode to start the rules engine.
  5. The engine will listen for updates from Redis, evaluate rules, and perform actions accordingly.

Development

Code Structure

  • cmd/rexc: Main application entry point for the compiler
  • cmd/rexd: Main application entry point for the runtime engine
  • pkg/compiler: Contains the bytecode compiler and related functions
  • pkg/runtime: Contains the runtime engine and related functions
  • pkg/scripting: Contains the scripting engine and related functions using Otto
  • pkg/store: Contains the Redis store implementation
  • pkg/logging: Contains logging utilities
  • tools/redis_setup: Redis setup and CLI tool
  • tools/rule_gen: Random rule generation tool

Defining Rules

Rules are defined in a JSON format. Each rule consists of conditions and actions. Here's an example:

{
  "rules": [
    {
      "name": "rule-1",
      "conditions": {
        "all": [
          { "fact": "weather:temperature", "operator": "GT", "value": 30 },
          { "fact": "weather:humidity", "operator": "LT", "value": 40.01 }
        ]
      },
      "actions": [
        {
          "type": "updateStore",
          "target": "weather:temperature_warning",
          "value": "high"
        }
      ]
    }
  ]
}

JSON Structure

The rules are defined in a JSON file with the following structure:

  • rules: an array of rule objects

Rule Object

A rule object has the following properties:

  • name: a unique string identifying the rule
  • priority: optional integer indicating the rule's priority (default: 10)
  • conditions: an object containing a single property:
  • ANY or ALL: an array of condition groups
  • actions: an array of action objects

Condition Group

A condition group is an object containing:

  • conditions: an array of condition objects.
  • operator: a string indicating the logical operator (ANY or ALL).

Condition Object

A condition object has the following properties:

  • fact: a string identifying the fact to evaluate. Based on the way Redis works, the recommendation is 'channel ' for the naming of facts.

  • operator: a string indicating the comparison operator (EQ, NEQ, LT, LTE, GT, GTE, CONTAINS, NOT_CONTAINS).

  • value: the value to compare against.

    **All condition objects not part of a grouping MUST be defined prior to any nested condition groups.

    **The characters in a string must NOT include a colon ':' due to how Redis parses channels/keys.

Action Object

An action object has the following properties:

  • type: a string indicating the action type ("updateStore" or "sendMessage") (sendMessage is not yet implemented).
  • fact: a string identifying the fact to update or send. Based on the way Redis works, the recommendation is 'channel ' for the naming of facts.
  • value: the value to update or send.
  • customProperty: an optional object containing custom properties for the action.

Scripting

REX supports scripting using the Otto JavaScript engine. Scripts can be defined and executed as part of the rule actions. This allows for more complex logic and calculations.

Defining Scripts

Scripts are defined in the Scripts section of a rule and can be referenced in actions. Here's an example:

{
  "rules": [
    {
      "name": "rule-1",
      "conditions": {
        "all": [{ "fact": "temperature", "operator": "GT", "value": 30 }]
      },
      "actions": [
        {
          "type": "updateStore",
          "target": "heat_index",
          "value": "{calculate_heat_index}"
        }
      ],
      "scripts": {
        "calculate_heat_index": {
          "params": ["temperature", "humidity"],
          "body": "return temperature * 1.8 + 32 + (humidity / 100) * 10;"
        }
      }
    }
  ]
}

Example JSON Ruleset with Scripts

{
  "rules": [
    {
      "name": "rule-1",
      "priority": 10,
      "conditions": {
        "all": [
          {
            "fact": "temperature",
            "operator": "GT",
            "value": 30.0
          },
          {
            "fact": "humidity",
            "operator": "LT",
            "value": 60
          },
          {
            "any": [
              {
                "fact": "pressure",
                "operator": "LT",
                "value": 1010
              },
              {
                "fact": "flow_rate",
                "operator": "GT",
                "value": 5.0
              }
            ]
          }
        ]
      },
      "actions": [
        {
          "type": "updateStore",
          "target": "temperature_status",
          "value": true
        },
        {
          "type": "updateStore",
          "target": "heat_index",
          "value": "{calculate_heat_index}"
        }
      ],
      "scripts": {
        "calculate_heat_index": {
          "params": ["temperature", "humidity"],
          "body": "return temperature * 1.8 + 32 + (humidity / 100) * 10;"
        }
      }
    },
    {
      "name": "rule-2",
      "priority": 5,
      "conditions": {
        "all": [
          {
            "any": [
              {
                "fact": "pressure",
                "operator": "EQ",
                "value": 1013
              },
              {
                "fact": "flow_rate",
                "operator": "GTE",
                "value": 5.0
              }
            ]
          },
          {
            "any": [
              {
                "fact": "temperature",
                "operator": "EQ",
                "value": 72
              },
              {
                "fact": "flow_rate",
                "operator": "LT",
                "value": 5.0
              }
            ]
          }
        ]
      },
      "actions": [
        {
          "type": "sendMessage",
          "target": "alert-service",
          "value": "Alert - Pressure or flow rate exceeded limits!"
        }
      ]
    }
  ]
}

Execution Order

Actions will be executed in the order they are defined in the rule.

Fact and Value Data Types

Facts are strings. Values can be strings surrounded by quotation marks (e.g. "fact_a"), bools (e.g. true or false), or numbers with or without decimal points (e.g. 30.01, 30, -12.123).

Priority Ties

Due to concurrent evaluations and other factors, no guarantees can be made regarding how priority ties are resolved. The engine will do its best to resolve all higher priority rules before lower ones, but no precedence can be guaranteed beyond that.

Testing

To run the tests:

go test ./...

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages