23113-yq01 Integration Guide
Sensor Overview
yq01 is integrated with ThinkLink through modbus-rtu in edgebus-dtu mode. This guide documents the current deployable artifacts and their source-traceable versions.
| Item | Value |
|---|---|
| Project | YQ-23113-yq01 |
| Vendor | YQ |
| Model | yq01 |
| Business Code | 23113 |
| Integration Mode | edgebus-dtu |
| Protocol | modbus-rtu |
Model catalog
Search for these models in ThinkLink by name or ID.
| Model type | Name | id_name | Platform model ID |
|---|---|---|---|
| Template | yq01 | 111755055119364096 | |
| Thing Model | [YQ01-EMETER] | yq01_emeter_23113 | 111754943219527680 |
| Thing Model | [YQ-23113-yq01资产] 区域聚合 | yq01_emeter_asset_23113 | 119677189950959626 |
| Thing Model | [YQ01-MCB] | yq01_mcb_23113 | 111754943676706816 |
Features and Application Scope
This project covers yq01 through modbus-rtu. Its capability boundary, fields, and deployable models follow the current descriptor, requirements, and model sources.
Deployment and Data Acquisition
| DTU | Not applicable | | Power | ac | | LoRaWAN Class | C | | Interface | RS-485 | | Baud Rate | 9600 |
Telemetry and Register Definitions
| Field | Name | Type | Unit | Address/Source | Description |
|---|---|---|---|---|---|
| voltage_a | voltage_a | uint16BE | V | reg100 (emeter) / reg256 (mcb) | Source field: voltage_a |
| voltage_b | voltage_b | uint16BE | V | reg101 | Source field: voltage_b |
| voltage_c | voltage_c | uint16BE | V | reg102 | Source field: voltage_c |
| current_a | current_a | uint32-lohi | A | — | Source field: current_a |
| current_b | current_b | uint32-lohi | A | — | Source field: current_b |
| current_c | current_c | uint32-lohi | A | — | Source field: current_c |
| active_power | active_power | int32-lohi | W | — | Source field: active_power |
| energy_import | energy_import | uint32-lohi | kWh | — | Source field: energy_import |
| run_status | run_status | uint16BE | reg515 (mcb only) | Source field: run_status |
Parameter Definitions
No project-specific parameters are defined.
Requirements Evidence
The following key facts were extracted from the requirements source; line references support verification.
| Category | Source Fact | Source |
|---|---|---|
| Protocol/Data | yq01 + Modbus RTU EdgeBus DTU SwVersion=30 | requirement/requirements.md:5 |
| Protocol/Data | - 485 EB does not support set/get para | requirement/requirements.md:10 |
| Protocol/Data | > 32 16 16 | requirement/requirements.md:38 |
EdgeBus Code
The following is the current EdgeBus business code.
import { Buffer } from "buffer";
import { buildOtaFile } from "@EBSDK/run";
import { EBModel } from "@EBSDK/EBCompiler/EBModel/EBModel";
import { EventInfoItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import type { UserConfUPItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import { CheckbitEnum, getOtaConfig } from "@EBSDK/otaConfig";
// ===== 用户配置区(按实际接线填写,每路设备调用一次)=====
const MODBUS_TT = (ebModel: EBModel) => {
readEmeter("0x01", "900s")
readMCB("0x02", "900s")
// readEmeter("0x03", "900s")
// readMCB("0x04", "900s")
return JSON.stringify(ebModel, null, 2)
}
// =======================================================
const otaConfig = getOtaConfig({
BaudRate: 9600,
StopBits: 1,
DataBits: 8,
Checkbit: CheckbitEnum.NONE,
Battery: false,
ConfirmDuty: 60,
BzType: 23113,
BzVersion: 1,
SwVersion: 30,
})
function readEmeter(addr: string, period: string): void {
const event: UserConfUPItem = {
name: `emeter_${addr}`,
port: 22,
version: "0x87",
dataType: "0x02",
upPeriod: period,
quInfo: [
// A/B/C 相电压 reg100-102
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "100", end: "102" }]
},
// A/B/C 相电流(低高各2寄存器)reg111-116
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "111", end: "116" }]
},
// 总有功功率(低/高)reg128-129
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "128", end: "129" }]
},
// 正向总有功电能(低/高)reg400-401
{
protocol: "modbus", addr, code: "0x03", period, isLast: true,
listVal: [{ start: "400", end: "401" }]
},
]
}
const item = new EventInfoItem(event)
item.upEventSetup()
item.eventInstall()
}
function readMCB(addr: string, period: string): void {
const event: UserConfUPItem = {
name: `mcb_${addr}`,
port: 22,
version: "0x87",
dataType: "0x03",
upPeriod: period,
quInfo: [
// A 相电压 reg256
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "256", end: "256" }]
},
// A 相电流(高/低)reg259-260
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "259", end: "260" }]
},
// 总有功功率(高/低)reg281-282
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "281", end: "282" }]
},
// 运行状态 reg515
{
protocol: "modbus", addr, code: "0x03", period, isLast: false,
listVal: [{ start: "515", end: "515" }]
},
// 正向总有功电能(低/高)reg770-771
{
protocol: "modbus", addr, code: "0x03", period, isLast: true,
listVal: [{ start: "770", end: "771" }]
},
]
}
const item = new EventInfoItem(event)
item.upEventSetup()
item.eventInstall()
}
buildOtaFile(import.meta.url, otaConfig, MODBUS_TT)Thing Model and RPC Code
Thing Model: [YQ01-EMETER]
id_name: yq01_emeter_23113 · ID: 111754943219527680
import { Buffer } from 'buffer'
import {PayloadParser, RPCHelper} from '#tklHelper'
function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
let frameInfo = {
port: 22,
dataLen: 33,
rssi: true,
status: 3,
battery: 4,
tagList: [
{ index: 0, tag: 0x87 },
{ index: 1, tag: 0x02 }
]
}
let appInfo = [
{ name: "A相电压", field_name: "voltage_a", unit: "V", index: 6, type: "uint16be", decimal: 1, coefficient: 0.1 },
{ name: "B相电压", field_name: "voltage_b", unit: "V", index: 8, type: "uint16be", decimal: 1, coefficient: 0.1 },
{ name: "C相电压", field_name: "voltage_c", unit: "V", index: 10, type: "uint16be", decimal: 1, coefficient: 0.1 },
]
let payParser = new PayloadParser({ device, msg, frameInfo, appInfo })
let tdata = payParser.telemetry()
if (tdata === null) return null
if ((tdata?.status & 0x02) === 0x02) {
const status = tdata.status
tdata = { ...(device.telemetry_data?.[thingModelId] ?? {}) }
tdata.status = status
tdata._modelName = "yq01_emeter_23113"
return { telemetry_data: tdata, server_attrs: null, shared_attrs: null }
}
const buf = Buffer.from(msg?.userdata?.payload, "base64")
// A相电流:低位reg111[12..13],高位reg112[14..15]
tdata.current_a = Number(((buf.readUInt16BE(14) * 65536 + buf.readUInt16BE(12)) * 0.001).toFixed(3))
// B相电流:低位reg113[16..17],高位reg114[18..19]
tdata.current_b = Number(((buf.readUInt16BE(18) * 65536 + buf.readUInt16BE(16)) * 0.001).toFixed(3))
// C相电流:低位reg115[20..21],高位reg116[22..23]
tdata.current_c = Number(((buf.readUInt16BE(22) * 65536 + buf.readUInt16BE(20)) * 0.001).toFixed(3))
// 总有功功率:低位reg128[24..25],高位reg129[26..27](有符号)
tdata.active_power = Number(((buf.readInt16BE(26) * 65536 + buf.readUInt16BE(24)) * 0.1).toFixed(1))
// 正向总有功电能:低位reg400[29..30],高位reg401[31..32]
tdata.energy_import = Number(((buf.readUInt16BE(31) * 65536 + buf.readUInt16BE(29)) * 0.01).toFixed(2))
tdata._modelName = "yq01_emeter_23113"
let result = { telemetry_data: tdata, server_attrs: null, shared_attrs: null };
if (result.telemetry_data && typeof result.telemetry_data === "object") result.telemetry_data.adr = msg?.userdata?.adr ?? null;
// ── rpcHook (设备对接通用规则) ──
if (typeof RPCHelper.buildRpcHookAction === "function") {
const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "yq01_emeter_23113", name: "[YQ01-EMETER]", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
}
return result;
}
console.log(payload_parser(
{},
{
"if": "loraWAN",
"gwrx": [{ "eui": "5a53012a000007da", "chan": 0, "lsnr": -6.8, "rfch": 1, "rssi": -98,
"time": "2026-05-26T10:00:00.000Z", "tmms": 0, "tmst": 0, "ftime": 0 }],
"type": "data", "token": 1,
"moteTx": { "codr": "4/5", "datr": "SF7BW125", "freq": 924.2, "modu": "LORA", "macAck": "", "macCmd": "" },
"geoInfo": { "type": "gw:ip", "accuracy": 10000, "altitude": 0, "latitude": 22.36497, "longitude": 114.12677 },
"moteeui": "0000000000000001",
"version": "3.0",
"userdata": {
"adr": false, "port": 22, "class": "ClassC", "seqno": 1,
"payload": "hwIAAAABCPwI/Aj8AfQAAAH0AAAB9AAASwAAAAAD6AAA",
"confirmed": false
}
},
"1",
{ telemetry_data: true },
{}
))Thing Model: yq01_emeter_asset_23113
id_name: yq01_emeter_asset_23113 · ID: 119677189950959626
import {PayloadParser, RPCHelper} from '#tklHelper';
// 资产(VIRTUAL)聚合物模型 — 通用 agg_spec 驱动聚合器,见 docs/references/workflow/asset-aggregation-model.md
// device=资产, msg=源设备快照, thingModelId=本物模型ID, noticeAttrs=变更标记。
// 聚合规格在资产 server_attrs.agg_spec:zone_voltage_a_avg=空间均值(voltage_a);zone_voltage_b_avg=空间均值(voltage_b);zone_voltage_c_avg=空间均值(voltage_c);zone_current_a_sum=时点累加(current_a);zone_current_b_sum=时点累加(current_b);zone_current_c_sum=时点累加(current_c);zone_active_power_sum=时点累加(active_power);zone_energy_import_sum=谨慎累加(energy_import);min_battery=取最小(battery);min_rssi=取最小(rssi);min_snr=取最小(snr);online_count=在线数(—)
function payload_parser(device, msg, thingModelId, noticeAttrs) {
if (!noticeAttrs || !noticeAttrs.telemetry_data) {
return { telemetry_data: null, server_attrs: null, shared_attrs: null };
}
const sa = device?.server_attrs || {};
const spec = sa.agg_spec || {};
const members = { ...(sa.members || {}) };
const staleMs = sa.stale_ms != null ? sa.stale_ms : 600000;
const now = (typeof Date !== "undefined" && Date.now) ? Date.now() : null;
// 合并源设备各物模型的最新快照为一个扁平对象
const srcTd = {};
const all = msg?.telemetry_data || {};
for (const k in all) { Object.assign(srcTd, all[k]); }
// 收集 spec 引用到的全部源字段,把本帧带到的值并进该成员快照
const want = {};
for (const out in spec) { if (spec[out].src) want[spec[out].src] = true; }
const got = {}; let touched = false;
for (const f in want) { if (srcTd[f] !== undefined && srcTd[f] !== null) { got[f] = srcTd[f]; touched = true; } }
if (touched && msg && msg.eui) {
const prev = (members[msg.eui] && members[msg.eui].v) || {};
const seq = (members[msg.eui] && members[msg.eui].ts) || 0;
members[msg.eui] = { v: Object.assign({}, prev, got), ts: now != null ? now : seq + 1 };
}
const euis = Object.keys(members);
const fresh = euis.filter(function (e) {
if (now == null || staleMs <= 0) return true;
const ts = members[e].ts;
return ts != null && ts <= now && (now - ts) <= staleMs;
});
const num = function (x) { return (x != null && !isNaN(x)) ? Number(x) : null; };
const out = {};
for (const field in spec) {
const op = spec[field].op, src = spec[field].src;
if (op === "count_online") { out[field] = fresh.length; continue; }
const pool = (op === "sum_cumul") ? euis : fresh;
const vals = pool.map(function (e) { return num(members[e].v && members[e].v[src]); }).filter(function (x) { return x != null; });
if (op === "avg") out[field] = vals.length ? Number((vals.reduce(function (a, b) { return a + b; }, 0) / vals.length).toFixed(2)) : null;
else if (op === "sum_snap" || op === "sum_cumul") out[field] = vals.length ? Number(vals.reduce(function (a, b) { return a + b; }, 0).toFixed(3)) : null;
else if (op === "max") out[field] = vals.length ? Math.max.apply(null, vals) : null;
else if (op === "min") out[field] = vals.length ? Math.min.apply(null, vals) : null;
else if (op === "or") out[field] = pool.some(function (e) { const x = members[e].v && members[e].v[src]; return x != null && x != 0; }) ? 1 : 0;
else if (op === "count_on") out[field] = pool.filter(function (e) { const x = members[e].v && members[e].v[src]; return x != null && x != 0; }).length;
else out[field] = null;
}
let result = { telemetry_data: out, server_attrs: { members: members }, shared_attrs: null };
// ── rpcHook (设备对接通用规则) ──
if (typeof RPCHelper.buildRpcHookAction === "function") {
const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "yq01_emeter_asset_23113", name: "[YQ-23113-yq01资产] 区域聚合", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
}
return result;
}
// --- local debug ---(两台成员先后上报;平台会把返回的 members 持久化回 device,这里手动模拟)
const agg_spec = {
"zone_voltage_a_avg": {
"op": "avg",
"src": "voltage_a"
},
"zone_voltage_b_avg": {
"op": "avg",
"src": "voltage_b"
},
"zone_voltage_c_avg": {
"op": "avg",
"src": "voltage_c"
},
"zone_current_a_sum": {
"op": "sum_snap",
"src": "current_a"
},
"zone_current_b_sum": {
"op": "sum_snap",
"src": "current_b"
},
"zone_current_c_sum": {
"op": "sum_snap",
"src": "current_c"
},
"zone_active_power_sum": {
"op": "sum_snap",
"src": "active_power"
},
"zone_energy_import_sum": {
"op": "sum_cumul",
"src": "energy_import"
},
"min_battery": {
"op": "min",
"src": "battery"
},
"min_rssi": {
"op": "min",
"src": "rssi"
},
"min_snr": {
"op": "min",
"src": "snr"
},
"online_count": {
"op": "count_online",
"src": ""
}
};
const device = { name: "YQ-23113-yq01资产-1", server_attrs: { agg_spec, stale_ms: 1800000, members: {} } };
const r1 = payload_parser(device, { eui: "MEMBER_A", telemetry_data: { m1: {"voltage_a":1,"voltage_b":1,"voltage_c":1,"current_a":1,"current_b":1,"current_c":1,"active_power":1,"energy_import":1,"battery":1,"rssi":1,"snr":1} } }, "tmid", { telemetry_data: true });
device.server_attrs.members = r1.server_attrs.members; // 平台持久化
console.log("成员A上报后:", JSON.stringify(r1.telemetry_data));
const r2 = payload_parser(device, { eui: "MEMBER_B", telemetry_data: { m1: {"voltage_a":2,"voltage_b":2,"voltage_c":2,"current_a":2,"current_b":2,"current_c":2,"active_power":2,"energy_import":2,"battery":2,"rssi":2,"snr":2} } }, "tmid", { telemetry_data: true });
device.server_attrs.members = r2.server_attrs.members;
console.log("成员B上报后(2台聚合):", JSON.stringify(r2.telemetry_data));
void PayloadParser;Thing Model: [YQ01-MCB]
id_name: yq01_mcb_23113 · ID: 111754943676706816
import { Buffer } from 'buffer'
import {PayloadParser, RPCHelper} from '#tklHelper'
// 帧布局(含2个大地址跳跃分隔字节,dataLen=24,待实机验证):
// [0]=0x87 [1]=0x03(MCB) [2]=covStatus [3]=status [4]=battery [5]=addr(0x02)
// [6..7] reg256 A相电压 ×0.01 V
// [8..9] reg259 A相电流高位
// [10..11] reg260 A相电流低位 ×0.01 A (与reg259组成UINT32)
// [12..13] reg281 总有功功率高位(有符号)
// [14..15] reg282 总有功功率低位 ×0.01 kW (与reg281组成INT32)
// [16] 分隔字节(reg282→reg515 跨度233)
// [17..18] reg515 运行状态
// [19] 分隔字节(reg515→reg770 跨度255)
// [20..21] reg770 正向总有功电能低位
// [22..23] reg771 正向总有功电能高位 ×0.01 kWh (与reg770组成UINT32)
function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
let frameInfo = {
port: 22,
dataLen: 24,
rssi: true,
status: 3,
battery: 4,
tagList: [
{ index: 0, tag: 0x87 },
{ index: 1, tag: 0x03 }
]
}
let appInfo = [
{ name: "A相电压", field_name: "voltage_a", unit: "V", index: 6, type: "uint16be", decimal: 2, coefficient: 0.01 }
]
let payParser = new PayloadParser({ device, msg, frameInfo, appInfo })
let tdata = payParser.telemetry()
if (tdata === null) return null
if ((tdata?.status & 0x02) === 0x02) {
const status = tdata.status
tdata = { ...(device.telemetry_data?.[thingModelId] ?? {}) }
tdata.status = status
tdata._modelName = "yq01_mcb_23113"
return { telemetry_data: tdata, server_attrs: null, shared_attrs: null }
}
const buf = Buffer.from(msg?.userdata?.payload, "base64")
// A相电流:高位 reg259[8..9],低位 reg260[10..11]
tdata.current_a = Number(((buf.readUInt16BE(8) * 65536 + buf.readUInt16BE(10)) * 0.01).toFixed(2))
// 总有功功率:高位 reg281[12..13](有符号),低位 reg282[14..15],单位 kW
tdata.active_power = Number(((buf.readInt16BE(12) * 65536 + buf.readUInt16BE(14)) * 0.01).toFixed(2))
// 运行状态:reg515[17..18](byte16 为分隔字节)
tdata.run_status = buf.readUInt16BE(17)
// 正向总有功电能:低位 reg770[20..21],高位 reg771[22..23](byte19 为分隔字节)
tdata.energy_import = Number(((buf.readUInt16BE(22) * 65536 + buf.readUInt16BE(20)) * 0.01).toFixed(2))
tdata._modelName = "yq01_mcb_23113"
let result = { telemetry_data: tdata, server_attrs: null, shared_attrs: null };
// ── rpcHook (设备对接通用规则) ──
if (typeof RPCHelper.buildRpcHookAction === "function") {
const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "yq01_mcb_23113", name: "[YQ01-MCB]", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
}
return result;
}
console.log(payload_parser(
{},
{
"if": "loraWAN",
"gwrx": [{ "eui": "5a53012a000007da", "chan": 0, "lsnr": 9.5, "rfch": 1, "rssi": -65,
"time": "2026-05-26T10:00:00.000Z", "tmms": 0, "tmst": 0, "ftime": 0 }],
"type": "data", "token": 1,
"moteTx": { "codr": "4/5", "datr": "SF7BW125", "freq": 924.2, "modu": "LORA", "macAck": "", "macCmd": "" },
"geoInfo": { "type": "gw:ip", "accuracy": 10000, "altitude": 0, "latitude": 22.36497, "longitude": 114.12677 },
"moteeui": "0000000000000002",
"version": "3.0",
"userdata": {
"adr": false, "port": 22, "class": "ClassC", "seqno": 1,
"payload": "hwMAAAACWUwAAAH0AAAAZwAAAQAAZAAA",
"confirmed": false
}
},
"1",
{ telemetry_data: true },
{}
))Thing Model Field Definitions
| Thing Model | Field | Type | Unit | Name |
|---|---|---|---|---|
| yq01_emeter_23113 | voltage_a | number | V | Phase A Voltage |
| yq01_emeter_23113 | voltage_b | number | V | Phase B Voltage |
| yq01_emeter_23113 | voltage_c | number | V | Phase C Voltage |
| yq01_emeter_23113 | current_a | number | A | Phase A Current |
| yq01_emeter_23113 | current_b | number | A | Phase B Current |
| yq01_emeter_23113 | current_c | number | A | Phase C Current |
| yq01_emeter_23113 | active_power | number | W | Total Active Power |
| yq01_emeter_23113 | energy_import | number | kWh | Import Active Energy |
| yq01_emeter_23113 | battery | number | v | Battery |
| yq01_emeter_23113 | rssi | number | dBm | RSSI |
| yq01_emeter_23113 | snr | number | dB | SNR |
| yq01_emeter_asset_23113 | zone_voltage_a_avg | number | V | Zone Phase A Voltage |
| yq01_emeter_asset_23113 | zone_voltage_b_avg | number | V | Zone Phase B Voltage |
| yq01_emeter_asset_23113 | zone_voltage_c_avg | number | V | Zone Phase C Voltage |
| yq01_emeter_asset_23113 | zone_current_a_sum | number | A | Zone Phase A Current |
| yq01_emeter_asset_23113 | zone_current_b_sum | number | A | Zone Phase B Current |
| yq01_emeter_asset_23113 | zone_current_c_sum | number | A | Zone Phase C Current |
| yq01_emeter_asset_23113 | zone_active_power_sum | number | kW | Zone Total Active Power |
| yq01_emeter_asset_23113 | zone_energy_import_sum | number | kWh | Zone Import Active Energy |
| yq01_emeter_asset_23113 | min_battery | number | v | Zone Battery |
| yq01_emeter_asset_23113 | min_rssi | number | dBm | Zone RSSI |
| yq01_emeter_asset_23113 | min_snr | number | dB | Zone SNR |
| yq01_emeter_asset_23113 | online_count | number | Zone Online Count | |
| yq01_mcb_23113 | voltage_a | number | V | Phase A Voltage |
| yq01_mcb_23113 | current_a | number | A | Phase A Current |
| yq01_mcb_23113 | active_power | number | kW | Total Active Power |
| yq01_mcb_23113 | run_status | number | Run Status | |
| yq01_mcb_23113 | energy_import | number | kWh | Import Active Energy |
| yq01_mcb_23113 | battery | number | v | Battery |
| yq01_mcb_23113 | rssi | number | dBm | RSSI |
| yq01_mcb_23113 | snr | number | dB | SNR |
RPC Parameter Definitions
No RPC input templates are defined.
Trigger Definitions
No project-specific triggers are defined.
Third-Party Platform Subscription
MQTT Topic: /v32/{Organization Account}/tkl/up/telemetry/{eui}
{
"eui": "6353012af10a9331",
"active_time": "2026-07-13T08:35:48.000Z",
"thingModelId": "Use the platform model ID from the model inventory",
"thingModelIdName": "Use the current telemetry model id_name",
"telemetry_data": {
"snr": 13.5,
"rssi": -51,
"battery": 3.37,
"voltage_a": 1,
"voltage_b": 2,
"voltage_c": 3,
"current_a": 4,
"current_b": 5,
"current_c": 6,
"active_power": 7,
"energy_import": 8
}
}Template Selection and Verification
Search ThinkLink by project name, business code, or the id_name and ID values in the model inventory. After deployment, use real uplinks to verify fields, units, RPCs, and trigger behavior.
Supplementary Notes
Requirements and decisions: requirement/requirements.md.