---
$schema: "http://json-schema.org/draft-07/schema#"
$id: "https://oimlsmart.org/schemas/R31.yaml"
title: "OIML R31 (Diaphragm gas meters) Certificate Schema"
description: |
  Schema for OIML R31:1995 diaphragm gas meters. The Recommendation defines
  accuracy classes 0.5/1/1.5/2 in the standard text; only class '1.5' appears
  in the dataset, and only on CN1 (China) certificates. FR2/NL1/RU1 issuers
  leave the array empty (the class is implied by the Qmin/Qmax ratio).

  Edition 1995. Categories: "Diaphragm gas meter", "Gas meter" — both forms
  appear in the data and refer to the same instrument type. The schema admits
  both pending canonicalization.

type: object
required: [certificate, certified_type, characteristics, recommendation]
properties:
  certificate:           { $ref: "_core.yaml#/definitions/Certificate" }
  issuing_authority:     { $ref: "_core.yaml#/definitions/IssuingAuthority" }
  applicants:            { $ref: "_core.yaml#/definitions/PartyList" }
  manufacturers:
    description: |
      Manufacturer parties. 12/39 R31 certs (all CN1) leave this empty.
      The `_core.yaml` PartyList requires minItems: 1; this local override
      permits an empty list for R31.
    type: array
    items: { $ref: "_core.yaml#/definitions/Party" }
  certified_type:        { $ref: "#/definitions/CertifiedType" }
  characteristics:       { $ref: "#/definitions/Characteristics" }
  recommendation:
    type: object
    required: [id, edition, scheme]
    properties:
      id:          { type: string, const: R31 }
      edition:     { type: integer, const: 1995 }
      amendment:   { type: [integer, string, "null"] }
      scheme:      { type: string, enum: [A, B] }
      accuracy_classes:
        description: |
          OIML R31:1995 accuracy classes. Only '1.5' appears in the dataset;
          27/39 certs leave this empty (the class is implied by Qmin/Qmax).
        type: array
        items: { $ref: "#/definitions/AccuracyClassValue" }
  test_reports:          { $ref: "_core.yaml#/definitions/TestReportList" }
  revision_history:      { $ref: "_core.yaml#/definitions/RevisionHistory" }
  model_family:          { $ref: "_core.yaml#/definitions/ModelFamily" }
  components:
    type: array
    items: { $ref: "_core.yaml#/definitions/Component" }
  footnotes:             { $ref: "_core.yaml#/definitions/FootnoteList" }
  d011_tests_applicable:
    description: |
      Tests mandated by R31:1995 type evaluation (D 11:2013 subset). R31 is a
      1995 Recommendation predating the modern D 11:2013 climatic test suite;
      the default list reflects the electronic-instrument tests typically
      applied during type evaluation of gas meters.
    type: array
    items: { $ref: "#/definitions/D011TestReference" }
    default:
    - { test_id: d011_t06_static_temperature }
    - { test_id: d011_t07_static_temperature }
    - { test_id: d011_t11_atmospheric_pressure }
    - { test_id: d011_t18_dc_mains_voltage_variation }
    - { test_id: d011_t20_ac_mains_voltage_variation }
    - { test_id: d011_t22_dc_mains_dips_interruptions }
    - { test_id: d011_t23_ac_mains_dips_interruptions }
    - { test_id: d011_t26_bursts_ac_dc_mains }
    - { test_id: d011_t27_surges_ac_dc_mains }
    - { test_id: d011_t28_bursts_signal_data_control }
    - { test_id: d011_t33_rf_em_fields_general }
    - { test_id: d011_t35_electrostatic_discharges }

definitions:
  AccuracyClassValue:
    description: |
      OIML R31:1995 §3.2 accuracy class token. The Recommendation uses decimal
      classes (0.5/1/1.5/2). Only '1.5' is observed in the dataset.
    type: string
    oneOf:
    - const: "0.5"
      description: Class 0.5 — highest precision diaphragm gas meter
    - const: "1"
      description: Class 1 — high precision
    - const: "1.5"
      description: Class 1.5 — medium precision (the only class observed in the dataset)
    - const: "2"
      description: Class 2 — coarse precision

  AccuracyClass:
    description: |
      StructuredValue-wrapped accuracy class token. Used at type_level when an
      issuer records the class against the characteristic; mirrors
      `recommendation.accuracy_classes`.

      Note: ~8 R31 CN1 certs attach `unit_symbol: "%"` to this value, which
      is semantically incorrect — the class is a designation, not a percentage.
      The schema permits any unit_symbol; a normalization pass is needed.
    allOf:
    - $ref: "_core.yaml#/definitions/StructuredValue"
    - properties:
        value: { $ref: "#/definitions/AccuracyClassValue" }

  DisplayType:
    description: |
      R31:1995 §3 meter display technology. The dataset records "mechanical"
      for traditional drum/dial indicators; some electronic variants exist.
      French-language data uses "mécanique". The schema accepts both
      canonical and observed tokens plus free-form strings pending
      canonicalization.
    allOf:
    - $ref: "_core.yaml#/definitions/StructuredValue"
    - properties:
        value:
          type: [string, "null"]
          enum: [mechanical, "mécanique", electromechanical, electronic, null]

  BooleanFlag:
    description: |
      R31:1995 optional-feature flags. Issuers record these in mixed languages:
      "oui"/"non" (French), "yes"/"no" (English), "true"/"false", or a numeric
      pulse count when the feature is present with a configurable rate.
      The schema admits any of these forms pending boolean normalization.
    allOf:
    - $ref: "_core.yaml#/definitions/StructuredValue"
    - properties:
        value:
          oneOf:
          - type: [string, "null"]
          - type: [number, "null"]

  CertifiedType:
    type: object
    required: [category, type_designations]
    properties:
      category:
        description: |
          OIML R31:1995 instrument category. Two forms appear in the dataset:
          "Diaphragm gas meter" (18/39, mostly FR2/NL1) and "Gas meter"
          (21/39, mostly CN1/older NL1). Both refer to the same instrument
          type; pending normalization to "Diaphragm gas meter".
        type: string
        enum:
        - Diaphragm gas meter
        - Gas meter
      type_designations:
        type: array
        items: { type: string }
      module_designation:
        description: |
          R31 meters are not classified by an instrument module; the dataset
          always records "N/A" or null here.
        type: [string, "null"]
        enum: ["N/A", "Not applicable", null]
      description: { type: [string, "null"] }

  Characteristics:
    type: object
    properties:
      type_level: { $ref: "#/definitions/TypeLevel" }
      model_level:
        type: array
        items: { $ref: "#/definitions/ModelLevelEntry" }
      config_level:
        type: array
        items: { $ref: "#/definitions/ConfigLevelEntry" }

  TypeLevel:
    description: |
      R31:1995 type-level characteristics. The dataset has heavy label
      variation (max_flow vs maximum_flow_rate vs maximum_capacity — all the
      same concept). The schema documents the canonical names and accepts
      legacy issuer-specific labels via `additionalProperties: true`.
    type: object
    properties:
      accuracy_class:                       { $ref: "#/definitions/AccuracyClass" }
      temperature_range:                    { $ref: "_modules/d011_environmental.yaml#/definitions/TemperatureRange" }
      humidity_class:                       { $ref: "_core.yaml#/definitions/StructuredValue" }
      electromagnetic_environment_class:    { $ref: "_modules/d011_environmental.yaml#/definitions/EMClass" }
      mechanical_environment_class:         { $ref: "_modules/d011_environmental.yaml#/definitions/MechanicalClass" }
      # Flow rates (canonical + observed synonyms)
      maximum_flow_rate:                    { $ref: "_core.yaml#/definitions/StructuredValue" }
      maximum_flow:                         { $ref: "_core.yaml#/definitions/StructuredValue" }
      maximum_capacity:                     { $ref: "_core.yaml#/definitions/StructuredValue" }
      minimum_flow_rate:                    { $ref: "_core.yaml#/definitions/StructuredValue" }
      minimum_flow:                         { $ref: "_core.yaml#/definitions/StructuredValue" }
      minimum_capacity:                     { $ref: "_core.yaml#/definitions/StructuredValue" }
      # Pressure (canonical + observed synonyms)
      maximum_operating_pressure:           { $ref: "_core.yaml#/definitions/StructuredValue" }
      maximum_pressure:                     { $ref: "_core.yaml#/definitions/StructuredValue" }
      maximum_use_pressure:                 { $ref: "_core.yaml#/definitions/StructuredValue" }
      # Cyclic volume (canonical + observed synonyms)
      cyclic_volume:                        { $ref: "_core.yaml#/definitions/StructuredValue" }
      v:                                    { $ref: "_core.yaml#/definitions/StructuredValue" }
      volume_v:                             { $ref: "_core.yaml#/definitions/StructuredValue" }
      # Display / indicator
      display_type:                         { $ref: "#/definitions/DisplayType" }
      type_of_display:                      { $ref: "#/definitions/DisplayType" }
      number_of_drums_digits:               { $ref: "_core.yaml#/definitions/StructuredValue" }
      number_of_dials_digits:               { $ref: "_core.yaml#/definitions/StructuredValue" }
      # Output
      pulse_generator:                      { $ref: "#/definitions/BooleanFlag" }
      pulse_generator_rate:                 { $ref: "_core.yaml#/definitions/StructuredValue" }
      pulse_frequency:                      { $ref: "_core.yaml#/definitions/StructuredValue" }
      number_of_output_axes:                { $ref: "_core.yaml#/definitions/StructuredValue" }
      # Optional features
      prepayment_device:                    { $ref: "#/definitions/BooleanFlag" }
      integrated_temperature_conversion:           { $ref: "#/definitions/BooleanFlag" }
      integrated_temperature_conversion_device:    { $ref: "#/definitions/BooleanFlag" }
    additionalProperties: true

  ModelLevelEntry:
    type: object
    required: [attribute, values]
    properties:
      attribute:    { type: string }
      unit:         { $ref: "_units.yaml#/definitions/Unit" }
      unit_symbol:  { type: [string, "null"] }
      values:
        type: array
        items:
          type: object
          required: [model, value]
          properties:
            model:            { type: string }
            value:            { $ref: "_core.yaml#/definitions/ValueOrScalar" }
            footnote_markers:
              type: array
              items: { type: string }

  ConfigLevelEntry:
    type: object
    required: [attribute, axis, values]
    properties:
      attribute:    { type: string }
      axis:      { oneOf: [ { type: string }, { type: "null" } ] }
      unit:         { $ref: "_units.yaml#/definitions/Unit" }
      unit_symbol:  { type: [string, "null"] }
      values:
        type: array
        items:
          type: object
          required: [condition, value]
          properties:
            condition: { type: string }
            value:     { $ref: "_core.yaml#/definitions/ValueOrScalar" }

  D011TestReference:
    description: Reference to an OIML D 11:2013 performance test.
    type: object
    required: [test_id]
    properties:
      test_id:          { type: string, pattern: '^d011_t\d{2}_' }
      test_level_index: { type: integer, minimum: 1, maximum: 5 }
      notes:            { type: string }

allOf:
- description: |
    D 11 damp heat tests (§9.2) apply iff the meter is installed in a
    humidity class that warrants them. For R31 the humidity_class field is
    free-form text rather than a D 11 H-class enum, so this rule applies
    only when an issuer records a D 11 H-class explicitly.
  if:
    properties:
      characteristics:
        properties:
          type_level:
            properties:
              humidity_class:
                properties:
                  value: { const: H2 }
  then:
    properties:
      d011_tests_applicable:
        contains:
          properties:
            test_id: { const: d011_t08_damp_heat_steady_state }