MiningOS Logo
Contribute To MiningOSAdd New Worker

Level 4 - Create a Device Type Template Worker

Level 4 workers define device category templates that serve as abstract bases for brand-specific implementations.

A.1. Repository Naming Convention

Level 4 repositories follow strict naming patterns (see Naming Conventions).

miningos-tpl-wrk-{devicetype}

Examples:

  • miningos-tpl-wrk-powermeter — Power monitoring equipment
  • miningos-tpl-wrk-miner — Mining hardware
  • miningos-tpl-wrk-sensor — Environmental sensors
  • miningos-tpl-wrk-container — Mining containers/racks

The tpl (template) designation distinguishes Level 4 workers from concrete implementations.

A.2. Directory Structure

A Level 4 template repository must follow this structure (see Repository Structure — Standard Directory Structure:

miningos-tpl-wrk-{devicetype}/
├── config/
│   ├── base.thing.json.example       Device-specific configuration template
│   ├── common.json.example           Common worker settings
│   └── facs/
│       ├── net.config.json.example   Network/RPC configuration
│       └── store.config.json.example  Storage configuration
├── mock/
│   └── mock-control-agent.js         Mock device for testing
├── tests/
│   ├── cases/
│   │   └── {feature}.js              Test case definitions
│   ├── schema/
│   │   └── {feature}.js              Test schema validators
│   ├── {devicetype}.spec.js          Main test file (*.spec.js pattern)
│   ├── executors.js                  Test executors
│   └── utils.js                      Test utilities
├── workers/
│   ├── lib/
│   │   ├── alerts.js                 Alert processing logic
│   │   ├── base.js                   Device base class (optional)
│   │   ├── constants.js              Device-type constants
│   │   ├── stats.js                  Statistics processing
│   │   └── utils.js                  Utility functions
│   └── rack.{devicetype}.wrk.js      Main template worker
├── LICENSE                            Apache-2.0
├── README.md                          Documentation
├── package.json                       Dependencies
├── setup-config.sh                    Configuration setup script
└── worker.js                          Entry point

For testing structure details, see Testing & Linting Guidelines — Test Directory Structure.

A.3. Package.json Structure

{
  "name": "miningos-tpl-wrk-{devicetype}",
  "version": "0.0.1",
  "description": "MiningOS Template Worker {DeviceType}",
  "author": {
    "name": "Your Name",
    "email": "your.email@example.com"
  },
  "maintainers": [
    {
      "name": "Your Name",
      "email": "your.email@example.com"
    }
  ],
  "keywords": ["miningos", "bitcoin", "mining"],
  "scripts": {
    "test": "brittle ./tests/**/*.spec.js",
    "lint": "standard",
    "lint:fix": "standard --fix"
  },
  "dependencies": {
    "async": "3.2.6",
    "bfx-svc-boot-js": "git+https://github.com/bitfinexcom/bfx-svc-boot-js.git",
    "miningos-tpl-wrk-thing": "git+https://github.com/tetherto/miningos-tpl-wrk-thing.git"
  },
  "devDependencies": {
    "brittle": "3.16.3",
    "miningos-mock-control-service": "git+https://github.com/tetherto/miningos-mock-control-service.git"
  },
  "engine": {
    "node": ">=16.0"
  },
  "license": "Apache-2.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/tetherto/miningos-tpl-wrk-{devicetype}.git"
  }
}

For linting and test script conventions, see Testing & Linting Guidelines.

A.4. Main Template Worker Implementation

The main worker file (workers/rack.\{devicetype\}.wrk.js) extends Level 3. For JSDoc annotation standards, see Code Documentation Standards:

'use strict'

const WrkRack = require('miningos-tpl-wrk-thing/workers/rack.thing.wrk')

class Wrk{DeviceType}Rack extends WrkRack {
  /**
   * Initialize the template worker.
   * Configure device-category-specific scheduling and facilities.
   */
  init () {
    super.init()
    
    // Configure additional statistics timeframes via code (optional)
    // Example: Real-time data collection every 5 seconds
    this.scheduleAddlStatTfs = [
      ['rtd', '*/5 * * * * *']  // Cron format: second granularity
    ]
    
    // Note: Additional timeframes can also be configured via config file
    // using `scheduleAddlStatConfigTfs` in base.thing.json
  }

  /**
   * Returns the base type identifier for this device category.
   * Level 5 implementations extend this with brand suffix.
   * @returns {string} Device type identifier
   */
  getThingType () {
    return '{devicetype}'  // e.g., 'powermeter', 'miner', 'sensor'
  }

  /**
   * Returns the base type for statistics aggregation.
   * Used by the stats system to group metrics.
   * @returns {string} Base type identifier
   */
  _getThingBaseType () {
    return '{devicetype}'
  }

  /**
   * Returns category-specific tags applied to all devices.
   * @returns {string[]} Array of tag strings
   */
  getSpecTags () {
    return ['{devicetype}']
  }
}

module.exports = Wrk{DeviceType}Rack

A.5. Abstract Methods Contract

Level 4 templates inherit abstract methods from Level 3 that must be implemented by Level 5 workers:

MethodPurposeBase BehaviorLevel 5 Requirement
async connectThing(thg)Establish connection to physical deviceNo-op (empty stub)Implement connection logic; return 0 on failure
async collectThingSnap(thg)Collect current device state snapshotThrows ERR_IMPL_UNKNOWNMust implement; return Object with stats and config
selectThingInfo()Select device info for API responsesReturns empty object \{\}Override to return device-specific details
async disconnectThing(thg)Gracefully disconnect from deviceCalls thg.ctrl.close() if availableOverride only if custom cleanup needed
async releaseIpThing(thg)Release IP/network resources for deviceNo-op (empty stub)Override if device requires IP resource cleanup

connectThing is a no-op stub in Level 3. Level 5 implementations should return 0 from connectThing when connection fails to signal the failure to the snapshot collection loop.

Optional Lifecycle Hooks:

HookWhen CalledBase Behavior
async registerThingHook0(thg)After new device registrationNo-op
async updateThingHook0(thg, thgPrev)After device configuration updateStores info changes to DB
async forgetThingHook0(thg)Before device removalNo-op
async setupThingHook0(thg)During device setup/loadingNo-op
async setupThingHook1(thg)After loading last state from DBNo-op
async collectSnapsHook0()After snapshot collection cycleNo-op
async tailLogHook0(logs, req)During log tail operationsNo-op

Optional Extension Methods:

MethodPurposeBase Behavior
_getWrkExtData(args)Provide custom worker extension data for getWrkExtData RPCReturns empty object \{\}

A.6. Statistics Timeframe Configuration

Level 4 templates can configure additional statistics collection timeframes through two mechanisms:

Code-Defined Timeframes (scheduleAddlStatTfs)

Set in the init() method for timeframes that are integral to the device type:

init () {
  super.init()
  
  // Real-time data collection every 5 seconds
  this.scheduleAddlStatTfs = [
    ['rtd', '*/5 * * * * *']
  ]
}

Config-Defined Timeframes (scheduleAddlStatConfigTfs)

Set in base.thing.json for operator-customizable timeframes:

{
  "thing": {
    "scheduleAddlStatConfigTfs": [
      ["custom-hourly", "0 0 * * * *"]
    ]
  }
}

Both arrays are merged with the default timeframes from miningos-lib-stats during startup.

A.7. Configuration Templates

For complete configuration file documentation, see Repository Structure — Key Files Explained.

base.thing.json.example

{
  "thing": {
    "{devicetype}DefaultPort": 502,
    "scheduleAddlStatConfigTfs": [],
    "{devicetype}": {
      "delay": 50,
      "timeout": 30000,
      "nominalValues": {
        "exampleMetric": 100
      }
    }
  }
}

common.json.example

{
  "debug": false,
  "logging": true
}

A.8. Testing Infrastructure

Level 4 templates must provide testing infrastructure that Level 5 implementations inherit. Test files must use the .spec.js extension to match the glob pattern in package.json. For complete testing guidelines, see Testing & Linting Guidelines.

tests/utils.js

'use strict'

const utils = require('miningos-tpl-wrk-thing/tests/utils')
const path = require('path')

// Extend parent test infrastructure
utils.SCHEMA_PATHS.push(path.join(__dirname, 'schema'))
utils.TEST_PATHS.push(path.join(__dirname, 'cases'))

module.exports = utils

tests/{devicetype}.spec.js

'use strict'

const utils = require('./utils')
const executors = require('./executors')

// Test suite inherits from parent and adds device-specific tests
utils.runTests(executors)

A.9. Worker Entry Point

worker.js

'use strict'

require('bfx-svc-boot-js')({
  wtype: 'wrk-{devicetype}-rack',
  conf: null
})

A.10. Level 4 Checklist

  • Repository created with correct naming (miningos-tpl-wrk-\{devicetype\})
  • Directory structure matches specification (see Repository Structure
  • package.json includes all required dependencies
  • package.json includes maintainers array
  • Test files use .spec.js extension
  • Main worker extends miningos-tpl-wrk-thing
  • getThingType() returns device category identifier
  • _getThingBaseType() returns base type for stats
  • getSpecTags() returns category tags
  • Configuration examples provided (including scheduleAddlStatConfigTfs)
  • Test infrastructure extends parent (see Testing & Linting Guidelines
  • README.md includes all required sections (see Code Documentation Standards)
  • Apache-2.0 license file included
  • setup-config.sh copies example configs

Level 4 - Create a Device Type Template Worker

Level 4 workers define device category templates that serve as abstract bases for brand-specific implementations.

A.1. Repository Naming Convention

Level 4 repositories follow strict naming patterns:

miningos-tpl-wrk-{devicetype}

Examples:

  • miningos-tpl-wrk-powermeter — Power monitoring equipment
  • miningos-tpl-wrk-miner — Mining hardware
  • miningos-tpl-wrk-sensor — Environmental sensors
  • miningos-tpl-wrk-container — Mining containers or racks

The tpl (template) designation distinguishes Level 4 workers from concrete implementations.

A.2. Directory Structure

A Level 4 template repository must follow this structure:

miningos-tpl-wrk-{devicetype}/
├── config/
│   ├── base.thing.json.example       Device-specific configuration template
│   ├── common.json.example           Common worker settings
│   └── facs/
│       ├── net.config.json.example   Network/RPC configuration
│       └── store.config.json.example  Storage configuration
├── mock/
│   └── mock-control-agent.js         Mock device for testing
├── tests/
│   ├── cases/
│   │   └── {feature}.js              Test case definitions
│   ├── schema/
│   │   └── {feature}.js              Test schema validators
│   ├── {devicetype}.spec.js          Main test file (*.spec.js pattern)
│   ├── executors.js                  Test executors
│   └── utils.js                      Test utilities
├── workers/
│   ├── lib/
│   │   ├── alerts.js                 Alert processing logic
│   │   ├── base.js                   Device base class (optional)
│   │   ├── constants.js              Device-type constants
│   │   ├── stats.js                  Statistics processing
│   │   └── utils.js                  Utility functions
│   └── rack.{devicetype}.wrk.js      Main template worker
├── LICENSE                            Apache-2.0
├── README.md                          Documentation
├── package.json                       Dependencies
├── setup-config.sh                    Configuration setup script
└── worker.js                          Entry point

A.3. Package.json Structure

To create a package.json file for a Level 4 template:

  1. Open a text editor and create the following JSON structure:

    {
      "name": "miningos-tpl-wrk-{devicetype}",
      "version": "0.0.1",
      "description": "MiningOS Template Worker {DeviceType}",
      "author": {
        "name": "Your Name",
        "email": "your.email@example.com"
      },
      "maintainers": [
        {
          "name": "Your Name",
          "email": "your.email@example.com"
        }
      ],
      "keywords": ["miningos", "bitcoin", "mining"],
      "scripts": {
        "test": "brittle ./tests/**/*.spec.js",
        "lint": "standard",
        "lint:fix": "standard --fix"
      },
      "dependencies": {
        "async": "3.2.6",
        "bfx-svc-boot-js": "git+https://github.com/bitfinexcom/bfx-svc-boot-js.git",
        "miningos-tpl-wrk-thing": "git+https://github.com/tetherto/miningos-tpl-wrk-thing.git"
      },
      "devDependencies": {
        "brittle": "3.16.3",
        "miningos-mock-control-service": "git+https://github.com/tetherto/miningos-mock-control-service.git"
      },
      "engine": {
        "node": ">=16.0"
      },
      "license": "Apache-2.0",
      "repository": {
        "type": "git",
        "url": "git+https://github.com/tetherto/miningos-tpl-wrk-{devicetype}.git"
      }
    }
  2. Replace \{devicetype\} with your specific device category (e.g., powermeter).

  3. Update the author and maintainers fields with your information.

  4. Save the file as package.json in your repository root.

A.4. Main Template Worker Implementation

The main worker file (workers/rack.\{devicetype\}.wrk.js) extends Level 3: To implement the main worker file:

  1. Create the file at workers/rack.\{devicetype\}.wrk.js.

  2. Add the following structure, replacing \{DeviceType\} with your device category (e.g., PowerMeter):

    'use strict'
    
    const WrkRack = require('miningos-tpl-wrk-thing/workers/rack.thing.wrk')
    
    class Wrk{DeviceType}Rack extends WrkRack {
      /**
       * Initialize the template worker.
       * Configure device-category-specific scheduling and facilities.
       */
      init () {
        super.init()
    
        // Configure additional statistics timeframes via code (optional)
        // Example: Real-time data collection every 5 seconds
        this.scheduleAddlStatTfs = [
          ['rtd', '*/5 * * * * *']  // Cron format: second granularity
        ]
    
        // Note: Additional timeframes can also be configured via config file
        // using `scheduleAddlStatConfigTfs` in base.thing.json
      }
    
      /**
       * Returns the base type identifier for this device category.
       * Level 5 implementations extend this with brand suffix.
       * @returns {string} Device type identifier
       */
      getThingType () {
        return '{devicetype}'  // e.g., 'powermeter', 'miner', 'sensor'
      }
    
      /**
       * Returns the base type for statistics aggregation.
       * Used by the stats system to group metrics.
       * @returns {string} Base type identifier
       */
      _getThingBaseType () {
        return '{devicetype}'
      }
    
      /**
       * Returns category-specific tags applied to all devices.
       * @returns {string[]} Array of tag strings
       */
      getSpecTags () {
        return ['{devicetype}']
      }
    }
    
    module.exports = Wrk{DeviceType}Rack

A.5. Abstract Methods Contract

Level 4 templates inherit abstract methods from Level 3 that must be implemented by Level 5 workers:

MethodPurposeBase BehaviorLevel 5 Requirement
async connectThing(thg)Establish connection to physical deviceNo-op (empty stub)Implement connection logic; return 0 on failure
async collectThingSnap(thg)Collect current device state snapshotThrows ERR_IMPL_UNKNOWNMust implement; return Object with stats and config
selectThingInfo()Select device info for API responsesReturns empty object \{\}Override to return device-specific details
async disconnectThing(thg)Gracefully disconnect from deviceCalls thg.ctrl.close() if availableOverride only if custom cleanup needed
async releaseIpThing(thg)Release IP/network resources for deviceNo-op (empty stub)Override if device requires IP resource cleanup

connectThing is a no-op stub in Level 3. Level 5 implementations should return 0 from connectThing when connection fails to signal the failure to the snapshot collection loop.

Optional Lifecycle Hooks:

HookWhen CalledBase Behavior
async registerThingHook0(thg)After new device registrationNo-op
async updateThingHook0(thg, thgPrev)After device configuration updateStores info changes to DB
async forgetThingHook0(thg)Before device removalNo-op
async setupThingHook0(thg)During device setup or loadingNo-op
async setupThingHook1(thg)After loading last state from DBNo-op
async collectSnapsHook0()After snapshot collection cycleNo-op
async tailLogHook0(logs, req)During log tail operationsNo-op

Optional Extension Methods:

MethodPurposeBase Behavior
_getWrkExtData(args)Provide custom worker extension data for getWrkExtData RPCReturns empty object \{\}

A.6. Statistics Timeframe Configuration

Level 4 templates can configure additional statistics collection timeframes through two mechanisms:

Code-Defined Timeframes (scheduleAddlStatTfs)

Set in the init() method for timeframes that are integral to the device type:

  1. In your main worker file, modify the init() method:

    init () {
      super.init()
    
      // Real-time data collection every 5 seconds
      this.scheduleAddlStatTfs = [
        ['rtd', '*/5 * * * * *']
      ]
    }

Config-Defined Timeframes (scheduleAddlStatConfigTfs)

Set in base.thing.json for operator-customizable timeframes:

  1. Edit your config/base.thing.json.example file:

    {
      "thing": {
        "scheduleAddlStatConfigTfs": [
          ["custom-hourly", "0 0 * * * *"]
        ]
      }
    }

Both arrays are merged with the default timeframes from miningos-lib-stats during startup.

A.7. Configuration Templates

base.thing.json.example

  1. Create or edit the config/base.thing.json.example file:

    {
      "thing": {
        "{devicetype}DefaultPort": 502,
        "scheduleAddlStatConfigTfs": [],
        "{devicetype}": {
          "delay": 50,
          "timeout": 30000,
          "nominalValues": {
            "exampleMetric": 100
          }
        }
      }
    }
  2. Replace \{devicetype\} with your device category.

common.json.example

  1. Create the config/common.json.example file:

    {
      "debug": false,
      "logging": true
    }

A.8. Testing Infrastructure

Level 4 templates must provide testing infrastructure that Level 5 implementations inherit. Test files must use the .spec.js extension to match the glob pattern in package.json.

tests/utils.js

To extend the parent test infrastructure:

  1. Create tests/utils.js:

    'use strict'
    
    const utils = require('miningos-tpl-wrk-thing/tests/utils')
    const path = require('path')
    
    // Extend parent test infrastructure
    utils.SCHEMA_PATHS.push(path.join(__dirname, 'schema'))
    utils.TEST_PATHS.push(path.join(__dirname, 'cases'))
    
    module.exports = utils

tests/{devicetype}.spec.js

To create the main test file create a tests/\{devicetype\}.spec.js file:

'use strict'

const utils = require('./utils')
const executors = require('./executors')

// Test suite inherits from parent and adds device-specific tests
utils.runTests(executors)

A.9. Worker Entry Point

worker.js

  1. Create the worker.js entry point file:

    'use strict'
    
    require('bfx-svc-boot-js')({
      wtype: 'wrk-{devicetype}-rack',
      conf: null
    })

A.10. Level 4 Checklist

  • Repository created with correct naming (miningos-tpl-wrk-\{devicetype\})
  • Directory structure matches specification
  • package.json includes all required dependencies
  • package.json includes maintainers array
  • Test files use .spec.js extension
  • Main worker extends miningos-tpl-wrk-thing
  • getThingType() returns device category identifier
  • _getThingBaseType() returns base type for stats
  • getSpecTags() returns category tags
  • Configuration examples provided (including scheduleAddlStatConfigTfs)
  • Test infrastructure extends parent
  • README.md includes all required sections
  • Apache-2.0 license file included
  • setup-config.sh copies example configs

On this page