{
  "name": "Role Manager",
  "status": "experimental",
  "shortId": "rolemanager",
  "camelName": "roleManager",
  "shortName": "roleManager",
  "extends": [
    "_base"
  ],
  "notes": {
    "short": "Assign roles to services on the Jacdac bus.",
    "long": "## Role allocation\n\nInternally, the role manager stores a mapping from role name to `(device_id, service_idx)`.\nUsers refer to services by using role names (eg., they instantiate an accelerometer client with a given role name).\nEach client has a role, and roles are unique to clients\n(ie., one should not have both a gyro and accelerometer service with role `left_leg`).\n\nThe simplest recommended automatic role assignment algorithm is as follows:\n\n```text\nroles.sort(strcmp() on UTF-8 representation of role name)\ndevices.sort(by device identifier (lexicographic on 8 byte string))\nmove self device to beginning of devices list\nfor every role\n  if role is not assigned\n    for every device\n      for every service on device\n        if serviceClass matches role\n          if service is not assigned to any role\n            assign service to role\n```\n\nDue to sorting, role names sharing a prefix will tend to be co-located on the single device.\nFor example, one can have roles `left_leg_acc`, `left_leg_gyro`, `right_leg_acc`, `right_leg_gyro`,\nand assuming combined gyro+accelerometer sensors, the pairs will tend to be allocated to a single leg,\nhowever the legs may be reversed.\nIn such a case the user can swap the physical sensors (note that left leg will always be assigned to\nsensor with smaller device identifier).\nAlternatively, the user can manually modify assignment using `set_role` command."
  },
  "classIdentifier": 508264038,
  "enums": {},
  "constants": {},
  "packets": [
    {
      "kind": "report",
      "name": "command_not_implemented",
      "identifier": 3,
      "description": "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.",
      "fields": [
        {
          "name": "service_command",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        },
        {
          "name": "packet_crc",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        }
      ],
      "identifierName": "command_not_implemented",
      "packFormat": "u16 u16",
      "derived": "_base"
    },
    {
      "kind": "const",
      "name": "instance_name",
      "identifier": 265,
      "description": "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).",
      "fields": [
        {
          "name": "_",
          "type": "string",
          "storage": 0
        }
      ],
      "optional": true,
      "identifierName": "instance_name",
      "packFormat": "s",
      "derived": "_base"
    },
    {
      "kind": "ro",
      "name": "status_code",
      "identifier": 259,
      "description": "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.",
      "fields": [
        {
          "name": "code",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        },
        {
          "name": "vendor_code",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        }
      ],
      "optional": true,
      "identifierName": "status_code",
      "packFormat": "u16 u16",
      "derived": "_base"
    },
    {
      "kind": "rw",
      "name": "client_variant",
      "identifier": 9,
      "description": "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.",
      "fields": [
        {
          "name": "_",
          "type": "string",
          "storage": 0
        }
      ],
      "optional": true,
      "identifierName": "client_variant",
      "packFormat": "s",
      "derived": "_base"
    },
    {
      "kind": "event",
      "name": "status_code_changed",
      "identifier": 4,
      "description": "Notifies that the status code of the service changed.",
      "fields": [
        {
          "name": "code",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        },
        {
          "name": "vendor_code",
          "type": "u16",
          "storage": 2,
          "isSimpleType": true
        }
      ],
      "optional": true,
      "identifierName": "status_code_changed",
      "packFormat": "u16 u16",
      "derived": "_base"
    },
    {
      "kind": "rw",
      "name": "auto_bind",
      "identifier": 128,
      "description": "Normally, if some roles are unfilled, and there are idle services that can fulfill them,\nthe brain device will assign roles (bind) automatically.\nSuch automatic assignment happens every second or so, and is trying to be smart about\nco-locating roles that share \"host\" (part before first slash),\nas well as reasonably stable assignments.\nOnce user start assigning roles manually using this service, auto-binding should be disabled to avoid confusion.",
      "fields": [
        {
          "name": "_",
          "type": "bool",
          "storage": 1,
          "defaultValue": 1
        }
      ],
      "packFormat": "u8"
    },
    {
      "kind": "ro",
      "name": "all_roles_allocated",
      "identifier": 385,
      "description": "Indicates if all required roles have been allocated to devices.",
      "fields": [
        {
          "name": "_",
          "type": "bool",
          "storage": 1
        }
      ],
      "packFormat": "u8"
    },
    {
      "kind": "command",
      "name": "set_role",
      "identifier": 129,
      "description": "Set role. Can set to empty to remove role binding.",
      "fields": [
        {
          "name": "device_id",
          "type": "devid",
          "storage": 8
        },
        {
          "name": "service_idx",
          "type": "u8",
          "storage": 1,
          "isSimpleType": true
        },
        {
          "name": "role",
          "type": "string",
          "storage": 0
        }
      ],
      "packFormat": "b[8] u8 s"
    },
    {
      "kind": "command",
      "name": "clear_all_roles",
      "identifier": 132,
      "description": "Remove all role bindings.",
      "fields": []
    },
    {
      "kind": "command",
      "name": "list_roles",
      "identifier": 131,
      "description": "List all roles and bindings required by the current program. `device_id` and `service_idx` are `0` if role is unbound.",
      "fields": [
        {
          "name": "roles",
          "type": "pipe",
          "storage": 12
        }
      ],
      "pipeType": "list_roles",
      "packFormat": "b[12]"
    },
    {
      "kind": "pipe_report",
      "name": "roles",
      "identifier": 0,
      "description": "List all roles and bindings required by the current program. `device_id` and `service_idx` are `0` if role is unbound.",
      "fields": [
        {
          "name": "device_id",
          "type": "devid",
          "storage": 8
        },
        {
          "name": "service_class",
          "type": "u32",
          "storage": 4,
          "isSimpleType": true
        },
        {
          "name": "service_idx",
          "type": "u8",
          "storage": 1,
          "isSimpleType": true
        },
        {
          "name": "role",
          "type": "string",
          "storage": 0
        }
      ],
      "pipeType": "list_roles",
      "packFormat": "b[8] u32 u8 s"
    },
    {
      "kind": "event",
      "name": "change",
      "identifier": 3,
      "description": "Notifies that role bindings have changed.",
      "fields": [],
      "identifierName": "change"
    }
  ],
  "tags": [
    "infrastructure"
  ]
}