diff --git a/app/Dockerfile b/app/Dockerfile index da84bdd..1699fc5 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -7,6 +7,7 @@ RUN npm ci COPY tsconfig.json ./ COPY public ./public +# Runtime mappers read JSON files from src/mappings, so they must be copied into the image. COPY src ./src CMD ["npm", "run", "dev"] diff --git a/app/package.json b/app/package.json index 57e4f5d..df3f589 100644 --- a/app/package.json +++ b/app/package.json @@ -3,10 +3,9 @@ "private": true, "type": "module", "scripts": { - "dev": "tsx watch src/index.ts", - "start": "tsx src/index.ts", + "dev": "tsx watch --env-file=.env src/index.ts", "build": "tsc", - "typecheck": "tsc --noEmit" + "start": "node --env-file=.env dist/index.js" }, "dependencies": { "@types/node": "^25.9.1", diff --git a/app/src/cloud-ingest.ts b/app/src/cloud-ingest.ts new file mode 100644 index 0000000..120c53c --- /dev/null +++ b/app/src/cloud-ingest.ts @@ -0,0 +1,15 @@ +const DEFAULT_CLOUD_INGEST_URL = 'http://localhost:3101/ingest' + +export const cloudIngestUrl = + process.env.CLOUD_INGEST_URL ?? DEFAULT_CLOUD_INGEST_URL + +export function postCloudIngest(body: unknown): Promise { + return fetch(cloudIngestUrl, { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-api-key': process.env.CLOUD_INGEST_API_KEY as string, + }, + body: JSON.stringify(body), + }) +} diff --git a/app/src/handlers/demo-plc.ts b/app/src/handlers/demo-plc.ts new file mode 100644 index 0000000..91b9fed --- /dev/null +++ b/app/src/handlers/demo-plc.ts @@ -0,0 +1,47 @@ +import { postCloudIngest } from '../cloud-ingest.js' +import { parseJson } from '../helpers/parse.js' +import type { TopicHandler } from '../types.js' + +export const TOPIC = 'data/demo/plc/v1' + +const EDGE = 'demo' + +type DemoPlcPayload = { + tag: string + value: number +} + +type DemoPlcMetric = { + edge: string + timestamp: number + tag: string + value: number +} + +function toMetric(payload: DemoPlcPayload, timestamp: number): DemoPlcMetric { + return { + edge: EDGE, + timestamp, + tag: payload.tag, + value: payload.value, + } +} + +async function postMetric(metric: DemoPlcMetric): Promise { + const response = await postCloudIngest([metric]) + + if (!response.ok) { + const text = await response.text() + throw new Error(text) + } + + console.log('demo.plc.posted', { tag: metric.tag }) +} + +export const handleDemoPlc: TopicHandler = async (topic, payload) => { + console.log('demo.plc.received', { topic, bytes: payload.length }) + + const value = parseJson(payload) + const metric = toMetric(value, Date.now()) + await postMetric(metric) +} diff --git a/app/src/handlers/edge5-modbus-v2.ts b/app/src/handlers/edge5-modbus-v2.ts new file mode 100644 index 0000000..2167fe4 --- /dev/null +++ b/app/src/handlers/edge5-modbus-v2.ts @@ -0,0 +1,45 @@ +import { postCloudIngest } from '../cloud-ingest.js' +import { createMetricMapper, type MappedMetric } from '../mapping.js' +import type { TopicHandler } from '../types.js' + +export const TOPIC = 'data/edge5/modbus/v2' + +const mapper = createMetricMapper({ + filePath: + process.env.EDGE5_MODBUS_V2_MAPPING_FILE ?? + new URL('../mappings/edge5-modbus.json', import.meta.url), +}) +const REGISTER_COUNT = mapper.tagCount + +function parseValues(payload: Buffer): number[] { + const value = JSON.parse(payload.toString('utf8')) as unknown + + if ( + Array.isArray(value) && + value.length === REGISTER_COUNT && + value.every((item) => typeof item === 'number' && Number.isFinite(item)) + ) { + return value + } + + throw new Error(`Expected JSON number[${REGISTER_COUNT}]`) +} + +async function postMetrics(metrics: MappedMetric[]): Promise { + const response = await postCloudIngest(metrics) + + if (!response.ok) { + const text = await response.text() + throw new Error(text) + } + + console.log('edge5.modbus.v2.posted', { count: metrics.length }) +} + +export const handleEdge5ModbusV2: TopicHandler = async (topic, payload) => { + console.log('edge5.modbus.v2.received', { topic, bytes: payload.length }) + + const values = parseValues(payload) + const metrics = mapper.mapValues(values, Date.now()) + await postMetrics(metrics) +} diff --git a/app/src/handlers/edge5-modbus.ts b/app/src/handlers/edge5-modbus.ts index 712a52e..5416393 100644 --- a/app/src/handlers/edge5-modbus.ts +++ b/app/src/handlers/edge5-modbus.ts @@ -1,11 +1,10 @@ +import { postCloudIngest } from '../cloud-ingest.js' import type { TopicHandler } from '../types.js' export const TOPIC = 'data/edge5/modbus/v1' const EDGE = 'edge5' const REGISTER_COUNT = 125 -const postUrl = - process.env.CLOUD_INGEST_URL ?? 'https://demo.backend.drill.greact.ru/api/ingest' type Edge5ModbusMetric = { edge: string @@ -44,14 +43,7 @@ function toMetrics(values: number[], timestamp: number): Edge5ModbusMetric[] { } async function postMetrics(metrics: Edge5ModbusMetric[]): Promise { - - const response = await fetch(postUrl, { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - body: JSON.stringify(metrics), - }) + const response = await postCloudIngest(metrics) if (!response.ok) { const text = await response.text() diff --git a/app/src/handlers/index.ts b/app/src/handlers/index.ts index 573acc8..c5f58c9 100644 --- a/app/src/handlers/index.ts +++ b/app/src/handlers/index.ts @@ -1,16 +1,13 @@ import { register } from '../registry.js' -import { handleTopic1, TOPIC as topic1 } from './topic1.js' -import { handleTopic2, TOPIC as topic2 } from './topic2.js' -import { handleTopic3, TOPIC as topic3 } from './topic3.js' -import { handleTopic4, TOPIC as topic4 } from './topic4.js' -import { handleTopic5, TOPIC as topic5 } from './topic5.js' +import { handleDemoPlc, TOPIC as demoPlc } from './demo-plc.js' import { handleEdge5Modbus, TOPIC as edge5Modbus } from './edge5-modbus.js' +import { + handleEdge5ModbusV2, + TOPIC as edge5ModbusV2, +} from './edge5-modbus-v2.js' import { handleEdge5Video, TOPIC as edge5Video } from './edge5-video.js' -register(topic1, handleTopic1) -register(topic2, handleTopic2) -register(topic3, handleTopic3) -register(topic4, handleTopic4) -register(topic5, handleTopic5) +register(demoPlc, handleDemoPlc) register(edge5Modbus, handleEdge5Modbus) +register(edge5ModbusV2, handleEdge5ModbusV2) register(edge5Video, handleEdge5Video) diff --git a/app/src/handlers/topic1.ts b/app/src/handlers/topic1.ts deleted file mode 100644 index fdde71b..0000000 --- a/app/src/handlers/topic1.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { parseJson } from '../helpers/parse.js' -import { isArrayOfChunks125 } from '../helpers/validate.js' -import type { TopicHandler } from '../types.js' - -export const TOPIC = 'poc/topic1' - -export const handleTopic1: TopicHandler = async (topic, payload) => { - const raw = parseJson(payload) - - if (!isArrayOfChunks125(raw)) { - console.warn('topic1.invalid_payload', { topic }) - return - } - - console.log('topic1.received', { topic, chunks: raw.length }) -} diff --git a/app/src/handlers/topic2.ts b/app/src/handlers/topic2.ts deleted file mode 100644 index a1f8d45..0000000 --- a/app/src/handlers/topic2.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { parseJson } from '../helpers/parse.js' -import { isArrayOfLength125 } from '../helpers/validate.js' -import type { TopicHandler } from '../types.js' - -export const TOPIC = 'poc/topic2' - -export const handleTopic2: TopicHandler = async (topic, payload) => { - const raw = parseJson(payload) - - if (!isArrayOfLength125(raw)) { - console.warn('topic2.invalid_payload', { topic }) - return - } - - console.log('topic2.received', { topic, length: raw.length }) -} diff --git a/app/src/handlers/topic3.ts b/app/src/handlers/topic3.ts deleted file mode 100644 index ccbd9d9..0000000 --- a/app/src/handlers/topic3.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { parseUtf8 } from '../helpers/parse.js' -import { isNonEmptyString } from '../helpers/validate.js' -import type { TopicHandler } from '../types.js' - -export const TOPIC = 'poc/topic3' - -export const handleTopic3: TopicHandler = async (topic, payload) => { - const text = parseUtf8(payload) - - if (!isNonEmptyString(text)) { - console.warn('topic3.invalid_payload', { topic }) - return - } - - console.log('topic3.received', { topic, text }) -} diff --git a/app/src/handlers/topic4.ts b/app/src/handlers/topic4.ts deleted file mode 100644 index 8eed63f..0000000 --- a/app/src/handlers/topic4.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { parseJson } from '../helpers/parse.js' -import { isFiniteNumber } from '../helpers/validate.js' -import type { TopicHandler } from '../types.js' - -export const TOPIC = 'poc/topic4' - -type Topic4Payload = { - value: number -} - -function isTopic4Payload(value: unknown): value is Topic4Payload { - return ( - typeof value === 'object' && - value !== null && - 'value' in value && - isFiniteNumber((value as Topic4Payload).value) - ) -} - -export const handleTopic4: TopicHandler = async (topic, payload) => { - const raw = parseJson(payload) - - if (!isTopic4Payload(raw)) { - console.warn('topic4.invalid_payload', { topic }) - return - } - - console.log('topic4.received', { topic, value: raw.value }) -} diff --git a/app/src/handlers/topic5.ts b/app/src/handlers/topic5.ts deleted file mode 100644 index de803df..0000000 --- a/app/src/handlers/topic5.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { parseJsonArray } from '../helpers/parse.js' -import type { TopicHandler } from '../types.js' - -export const TOPIC = 'poc/topic5' - -export const handleTopic5: TopicHandler = async (topic, payload) => { - const items = parseJsonArray(payload) - - console.log('topic5.received', { topic, count: items.length }) -} diff --git a/app/src/mapping.ts b/app/src/mapping.ts new file mode 100644 index 0000000..929f98c --- /dev/null +++ b/app/src/mapping.ts @@ -0,0 +1,46 @@ +import { readFileSync } from 'node:fs' + +export type MappedMetric = { + edge: string + timestamp: number + tag: string + value: number +} + +type MappingConfig = { + edge: string + tags: string[] +} + +export type MetricMapper = { + edge: string + tagCount: number + mapValues: (values: number[], timestamp: number) => MappedMetric[] +} + +type MetricMapperOptions = { + // filePath contract: + // - built-in mappings are passed as URL via new URL('../mappings/*.json', import.meta.url); + // - env overrides are expected as absolute paths inside the runtime container. + filePath: string | URL +} + +function readMappingConfig(filePath: string | URL): MappingConfig { + return JSON.parse(readFileSync(filePath, 'utf8')) as MappingConfig +} + +export function createMetricMapper(options: MetricMapperOptions): MetricMapper { + const config = readMappingConfig(options.filePath) + + return { + edge: config.edge, + tagCount: config.tags.length, + mapValues: (values, timestamp) => + values.map((value, index) => ({ + edge: config.edge, + timestamp, + tag: config.tags[index], + value, + })), + } +} diff --git a/app/src/mappings/edge5-modbus.json b/app/src/mappings/edge5-modbus.json new file mode 100644 index 0000000..c8f645e --- /dev/null +++ b/app/src/mappings/edge5-modbus.json @@ -0,0 +1,130 @@ +{ + "edge": "edge5", + "tags": [ + "hook_weight_1", + "hookblock_position", + "manifold_pressure_1", + "hydraulic_tong_torque", + "hook_weight_2", + "outlet_flow", + "gas_1_lfl", + "gas_1_h2s", + "gas_2_h2s", + "gas_3_h2s", + "outlet_temperature", + "inlet_flow_1", + "akb_tong_torque", + "rotary_torque", + "rotary_rpm", + "manifold_pressure_2", + "machine_tong_torque", + "inlet_temperature", + "gas_4_h2s", + "density_1", + "density_2", + "density_3", + "density_4", + "density_5", + "density_6", + "level_1", + "level_2", + "level_3", + "level_4", + "level_5", + "level_6", + "volume_1", + "volume_2", + "volume_3", + "volume_4", + "volume_5", + "volume_6", + "total_volume", + "weight_on_bit", + "penetration_rate", + "tripping_speed", + "tool_depth", + "bottom_depth", + "outlet_flow_change", + "stand_feed", + "lowered_stands_count", + "inlet_flow_2", + "slips_position", + "gas_2_lfl", + "level_7", + "volume_7", + "top_drive_rpm", + "top_drive_torque", + "level_8", + "volume_8", + "pump_1_strokes", + "pump_2_strokes", + "pump_strokes_total", + "gas_3_lfl", + "pump_3_strokes", + "level_9", + "volume_9", + "level_10", + "volume_10", + "level_11", + "volume_11", + "level_12", + "volume_12", + "level_13", + "volume_13", + "level_14", + "volume_14", + "total_volume_change", + "casing_tong_1_torque", + "casing_tong_2_torque", + "drill_pipe_tong_hydraulic_pressure", + "drill_pipe_tong_high_low_speed", + "hydraulic_distributor_section_1", + "hydraulic_distributor_section_2", + "tong_type", + "gas_4_lfl", + "gas_5_lfl", + "gas_6_lfl", + "gas_7_lfl", + "gas_8_lfl", + "guyline_tension_1", + "guyline_tension_2", + "guyline_tension_3", + "guyline_tension_4", + "gas_9_lfl_ch4", + "gas_10_lfl_ch4", + "gas_1_lfl_c3h8", + "gas_2_lfl_c3h8", + "gas_3_lfl_c3h8", + "gas_4_lfl_c3h8", + "gas_5_lfl_c3h8", + "gas_6_lfl_c3h8", + "gas_7_lfl_c3h8", + "gas_8_lfl_c3h8", + "gas_9_lfl_c3h8", + "gas_10_lfl_c3h8", + "rotary_torque_2", + "density_7", + "density_8", + "density_9", + "density_10", + "bit_position_above_bottom", + "disperser_pump_pressure", + "disperser_pump_strokes", + "disperser_pump_outlet_flow", + "pump_1_outlet_flow", + "pump_2_outlet_flow", + "drilling_line_runtime", + "tripping_fill_volume_total", + "pump_1_outlet_pressure", + "pump_2_outlet_pressure", + "drilling_time_per_meter", + "gas_11_lfl_ch4", + "gas_12_lfl_ch4", + "gas_13_lfl_ch4", + "pump_3_outlet_flow", + "pump_3_outlet_pressure", + "gas_5_h2s", + "gas_6_h2s", + "gas_7_h2s" + ] +} diff --git a/app/src/mappings/edge5-modbus_rus.json b/app/src/mappings/edge5-modbus_rus.json new file mode 100644 index 0000000..b5ef432 --- /dev/null +++ b/app/src/mappings/edge5-modbus_rus.json @@ -0,0 +1,130 @@ +{ + "edge": "edge5", + "tags": [ + "Вес на крюке 1", + "Положение крюкоблока", + "Давление в манифольде 1", + "Момент на гидроключе", + "Вес на крюке 2", + "Расход на выходе", + "Загазованность 1 (НКПР)", + "Загазованность 1 (H2S)", + "Загазованность 2 (H2S)", + "Загазованность 3 (H2S)", + "Температура на выходе", + "Расход на входе 1", + "Момент на ключе АКБ", + "Момент на роторе", + "Обороты ротора", + "Давление в манифольде 2", + "Момент на машинном ключе", + "Температура на входе", + "Загазованность 4 (H2S)", + "Плотность 1", + "Плотность 2", + "Плотность 3", + "Плотность 4", + "Плотность 5", + "Плотность 6", + "Уровень 1", + "Уровень 2", + "Уровень 3", + "Уровень 4", + "Уровень 5", + "Уровень 6", + "Объем 1", + "Объем 2", + "Объем 3", + "Объем 4", + "Объем 5", + "Объем 6", + "Объем суммарный", + "Нагрузка на долото", + "Скорость проходки", + "Скорость СПО", + "Глубина инструмента", + "Глубина забоя", + "Изм. расхода на выходе", + "Подача свечи", + "Количество опущенных свеч", + "Расход на входе 2", + "Положение клиньев", + "Загазованность 2 (НКПР)", + "Уровень 7", + "Объем 7", + "Обороты СВП", + "Крутящий момент СВП", + "Уровень 8", + "Объем 8", + "Ходы насоса 1", + "Ходы насоса 2", + "Сумма ходов насосов", + "Загазованность 3 (НКПР)", + "Ходы насоса 3", + "Уровень 9", + "Объем 9", + "Уровень 10", + "Объем 10", + "Уровень 11", + "Объем 11", + "Уровень 12", + "Объем 12", + "Уровень 13", + "Объем 13", + "Уровень 14", + "Объем 14", + "Изменение суммарного объема (±)", + "Момент на ключе 1 обсадных труб", + "Момент на ключе 2 обсадных труб", + "Давление в гидросистеме ключа для бурильных труб", + "Высокая/низкая скорость ключа бурильных труб", + "Секция 1 гидрораспределителя", + "Секция 2 гидрораспределителя", + "Тип ключа", + "Загазованность 4 (НКПР)", + "Загазованность 5 (НКПР)", + "Загазованность 6 (НКПР)", + "Загазованность 7 (НКПР)", + "Загазованность 8 (НКПР)", + "Усилие в растяжке 1", + "Усилие в растяжке 2", + "Усилие в растяжке 3", + "Усилие в растяжке 4", + "Загазованность 9 (НКПР) CH4", + "Загазованность 10 (НКПР) CH4", + "Загазованность 1 (НКПР) C3H8", + "Загазованность 2 (НКПР) C3H8", + "Загазованность 3 (НКПР) C3H8", + "Загазованность 4 (НКПР) C3H8", + "Загазованность 5 (НКПР) C3H8", + "Загазованность 6 (НКПР) C3H8", + "Загазованность 7 (НКПР) C3H8", + "Загазованность 8 (НКПР) C3H8", + "Загазованность 9 (НКПР) C3H8", + "Загазованность 10 (НКПР) C3H8", + "Момент на роторе 2", + "Плотность 7", + "Плотность 8", + "Плотность 9", + "Плотность 10", + "Положение долота над забоем", + "Давление насоса диспергации", + "Число ходов насоса диспергации", + "Расход на выходе насоса диспергации", + "Расход на выходе насоса 1", + "Расход на выходе насоса 2", + "Наработка талевого каната (в реальном времени)", + "Суммарный объем долива скважины при СПО", + "Давление на выходе насоса 1", + "Давление на выходе насоса 2", + "Время бурения 1м проходки", + "Загазованность 11 (НКПР) CH4", + "Загазованность 12 (НКПР) CH4", + "Загазованность 13 (НКПР) CH4", + "Расход на выходе насоса 3", + "Давление на выходе насоса 3", + "Загазованность 5 (H2S)", + "Загазованность 6 (H2S)", + "Загазованность 7 (H2S)" + ] +} diff --git a/app/src/mappings/edge5-modbus_unit.json b/app/src/mappings/edge5-modbus_unit.json new file mode 100644 index 0000000..411d711 --- /dev/null +++ b/app/src/mappings/edge5-modbus_unit.json @@ -0,0 +1,630 @@ +{ + "edge": "edge5", + "tags": [ + { + "key": "hook_weight_1", + "name": "Вес на крюке 1", + "unit": "тс" + }, + { + "key": "hookblock_position", + "name": "Положение крюкоблока", + "unit": "м" + }, + { + "key": "manifold_pressure_1", + "name": "Давление в манифольде 1", + "unit": "МПа" + }, + { + "key": "hydraulic_tong_torque", + "name": "Момент на гидроключе", + "unit": "кг*м" + }, + { + "key": "hook_weight_2", + "name": "Вес на крюке 2", + "unit": "тс" + }, + { + "key": "outlet_flow", + "name": "Расход на выходе", + "unit": "%" + }, + { + "key": "gas_1_lfl", + "name": "Загазованность 1 (НКПР)", + "unit": "%" + }, + { + "key": "gas_1_h2s", + "name": "Загазованность 1 (H2S)", + "unit": "мг/м3" + }, + { + "key": "gas_2_h2s", + "name": "Загазованность 2 (H2S)", + "unit": "мг/м3" + }, + { + "key": "gas_3_h2s", + "name": "Загазованность 3 (H2S)", + "unit": "мг/м3" + }, + { + "key": "outlet_temperature", + "name": "Температура на выходе", + "unit": "гр.C" + }, + { + "key": "inlet_flow_1", + "name": "Расход на входе 1", + "unit": "л/сек" + }, + { + "key": "akb_tong_torque", + "name": "Момент на ключе АКБ", + "unit": "кН*м" + }, + { + "key": "rotary_torque", + "name": "Момент на роторе", + "unit": "кН*м" + }, + { + "key": "rotary_rpm", + "name": "Обороты ротора", + "unit": "об/мин" + }, + { + "key": "manifold_pressure_2", + "name": "Давление в манифольде 2", + "unit": "МПа" + }, + { + "key": "machine_tong_torque", + "name": "Момент на машинном ключе", + "unit": "кН*м" + }, + { + "key": "inlet_temperature", + "name": "Температура на входе", + "unit": "гр.C" + }, + { + "key": "gas_4_h2s", + "name": "Загазованность 4 (H2S)", + "unit": "мг/м3" + }, + { + "key": "density_1", + "name": "Плотность 1", + "unit": "г/см3" + }, + { + "key": "density_2", + "name": "Плотность 2", + "unit": "г/см3" + }, + { + "key": "density_3", + "name": "Плотность 3", + "unit": "г/см3" + }, + { + "key": "density_4", + "name": "Плотность 4", + "unit": "г/см3" + }, + { + "key": "density_5", + "name": "Плотность 5", + "unit": "г/см3" + }, + { + "key": "density_6", + "name": "Плотность 6", + "unit": "г/см3" + }, + { + "key": "level_1", + "name": "Уровень 1", + "unit": "см" + }, + { + "key": "level_2", + "name": "Уровень 2", + "unit": "см" + }, + { + "key": "level_3", + "name": "Уровень 3", + "unit": "см" + }, + { + "key": "level_4", + "name": "Уровень 4", + "unit": "см" + }, + { + "key": "level_5", + "name": "Уровень 5", + "unit": "см" + }, + { + "key": "level_6", + "name": "Уровень 6", + "unit": "см" + }, + { + "key": "volume_1", + "name": "Объем 1", + "unit": "м3" + }, + { + "key": "volume_2", + "name": "Объем 2", + "unit": "м3" + }, + { + "key": "volume_3", + "name": "Объем 3", + "unit": "м3" + }, + { + "key": "volume_4", + "name": "Объем 4", + "unit": "м3" + }, + { + "key": "volume_5", + "name": "Объем 5", + "unit": "м3" + }, + { + "key": "volume_6", + "name": "Объем 6", + "unit": "м3" + }, + { + "key": "total_volume", + "name": "Объем суммарный", + "unit": "м3" + }, + { + "key": "weight_on_bit", + "name": "Нагрузка на долото", + "unit": "тс" + }, + { + "key": "penetration_rate", + "name": "Скорость проходки", + "unit": "м/ч" + }, + { + "key": "tripping_speed", + "name": "Скорость СПО", + "unit": "м/с" + }, + { + "key": "tool_depth", + "name": "Глубина инструмента", + "unit": "м" + }, + { + "key": "bottom_depth", + "name": "Глубина забоя", + "unit": "м" + }, + { + "key": "outlet_flow_change", + "name": "Изм. расхода на выходе", + "unit": "%" + }, + { + "key": "stand_feed", + "name": "Подача свечи", + "unit": "м" + }, + { + "key": "lowered_stands_count", + "name": "Количество опущенных свеч", + "unit": "" + }, + { + "key": "inlet_flow_2", + "name": "Расход на входе 2", + "unit": "л/сек" + }, + { + "key": "slips_position", + "name": "Положение клиньев", + "unit": "" + }, + { + "key": "gas_2_lfl", + "name": "Загазованность 2 (НКПР)", + "unit": "%" + }, + { + "key": "level_7", + "name": "Уровень 7", + "unit": "см" + }, + { + "key": "volume_7", + "name": "Объем 7", + "unit": "м3" + }, + { + "key": "top_drive_rpm", + "name": "Обороты СВП", + "unit": "об/мин" + }, + { + "key": "top_drive_torque", + "name": "Крутящий момент СВП", + "unit": "кНм" + }, + { + "key": "level_8", + "name": "Уровень 8", + "unit": "см" + }, + { + "key": "volume_8", + "name": "Объем 8", + "unit": "м3" + }, + { + "key": "pump_1_strokes", + "name": "Ходы насоса 1", + "unit": "ход/мин" + }, + { + "key": "pump_2_strokes", + "name": "Ходы насоса 2", + "unit": "ход/мин" + }, + { + "key": "pump_strokes_total", + "name": "Сумма ходов насосов", + "unit": "ход/мин" + }, + { + "key": "gas_3_lfl", + "name": "Загазованность 3 (НКПР)", + "unit": "%" + }, + { + "key": "pump_3_strokes", + "name": "Ходы насоса 3", + "unit": "ход/мин" + }, + { + "key": "level_9", + "name": "Уровень 9", + "unit": "см" + }, + { + "key": "volume_9", + "name": "Объем 9", + "unit": "м3" + }, + { + "key": "level_10", + "name": "Уровень 10", + "unit": "см" + }, + { + "key": "volume_10", + "name": "Объем 10", + "unit": "м3" + }, + { + "key": "level_11", + "name": "Уровень 11", + "unit": "см" + }, + { + "key": "volume_11", + "name": "Объем 11", + "unit": "м3" + }, + { + "key": "level_12", + "name": "Уровень 12", + "unit": "см" + }, + { + "key": "volume_12", + "name": "Объем 12", + "unit": "м3" + }, + { + "key": "level_13", + "name": "Уровень 13", + "unit": "см" + }, + { + "key": "volume_13", + "name": "Объем 13", + "unit": "м3" + }, + { + "key": "level_14", + "name": "Уровень 14", + "unit": "см" + }, + { + "key": "volume_14", + "name": "Объем 14", + "unit": "м3" + }, + { + "key": "total_volume_change", + "name": "Изменение суммарного объема (±)", + "unit": "м3" + }, + { + "key": "casing_tong_1_torque", + "name": "Момент на ключе 1 обсадных труб", + "unit": "кгм" + }, + { + "key": "casing_tong_2_torque", + "name": "Момент на ключе 2 обсадных труб", + "unit": "кгм" + }, + { + "key": "drill_pipe_tong_hydraulic_pressure", + "name": "Давление в гидросистеме ключа для бурильных труб", + "unit": "МПа" + }, + { + "key": "drill_pipe_tong_high_low_speed", + "name": "Высокая/низкая скорость ключа бурильных труб", + "unit": "" + }, + { + "key": "hydraulic_distributor_section_1", + "name": "Секция 1 гидрораспределителя", + "unit": "" + }, + { + "key": "hydraulic_distributor_section_2", + "name": "Секция 2 гидрораспределителя", + "unit": "" + }, + { + "key": "tong_type", + "name": "Тип ключа", + "unit": "" + }, + { + "key": "gas_4_lfl", + "name": "Загазованность 4 (НКПР)", + "unit": "%" + }, + { + "key": "gas_5_lfl", + "name": "Загазованность 5 (НКПР)", + "unit": "%" + }, + { + "key": "gas_6_lfl", + "name": "Загазованность 6 (НКПР)", + "unit": "%" + }, + { + "key": "gas_7_lfl", + "name": "Загазованность 7 (НКПР)", + "unit": "%" + }, + { + "key": "gas_8_lfl", + "name": "Загазованность 8 (НКПР)", + "unit": "%" + }, + { + "key": "guyline_tension_1", + "name": "Усилие в растяжке 1", + "unit": "кг" + }, + { + "key": "guyline_tension_2", + "name": "Усилие в растяжке 2", + "unit": "кг" + }, + { + "key": "guyline_tension_3", + "name": "Усилие в растяжке 3", + "unit": "кг" + }, + { + "key": "guyline_tension_4", + "name": "Усилие в растяжке 4", + "unit": "кг" + }, + { + "key": "gas_9_lfl_ch4", + "name": "Загазованность 9 (НКПР) CH4", + "unit": "%" + }, + { + "key": "gas_10_lfl_ch4", + "name": "Загазованность 10 (НКПР) CH4", + "unit": "%" + }, + { + "key": "gas_1_lfl_c3h8", + "name": "Загазованность 1 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_2_lfl_c3h8", + "name": "Загазованность 2 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_3_lfl_c3h8", + "name": "Загазованность 3 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_4_lfl_c3h8", + "name": "Загазованность 4 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_5_lfl_c3h8", + "name": "Загазованность 5 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_6_lfl_c3h8", + "name": "Загазованность 6 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_7_lfl_c3h8", + "name": "Загазованность 7 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_8_lfl_c3h8", + "name": "Загазованность 8 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_9_lfl_c3h8", + "name": "Загазованность 9 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "gas_10_lfl_c3h8", + "name": "Загазованность 10 (НКПР) C3H8", + "unit": "%" + }, + { + "key": "rotary_torque_2", + "name": "Момент на роторе 2", + "unit": "кН*м" + }, + { + "key": "density_7", + "name": "Плотность 7", + "unit": "г/см3" + }, + { + "key": "density_8", + "name": "Плотность 8", + "unit": "г/см3" + }, + { + "key": "density_9", + "name": "Плотность 9", + "unit": "г/см3" + }, + { + "key": "density_10", + "name": "Плотность 10", + "unit": "г/см3" + }, + { + "key": "bit_position_above_bottom", + "name": "Положение долота над забоем", + "unit": "м" + }, + { + "key": "disperser_pump_pressure", + "name": "Давление насоса диспергации", + "unit": "МПа" + }, + { + "key": "disperser_pump_strokes", + "name": "Число ходов насоса диспергации", + "unit": "ход/мин" + }, + { + "key": "disperser_pump_outlet_flow", + "name": "Расход на выходе насоса диспергации", + "unit": "л/с" + }, + { + "key": "pump_1_outlet_flow", + "name": "Расход на выходе насоса 1", + "unit": "л/с" + }, + { + "key": "pump_2_outlet_flow", + "name": "Расход на выходе насоса 2", + "unit": "л/с" + }, + { + "key": "drilling_line_runtime", + "name": "Наработка талевого каната (в реальном времени)", + "unit": "час." + }, + { + "key": "tripping_fill_volume_total", + "name": "Суммарный объем долива скважины при СПО", + "unit": "м3" + }, + { + "key": "pump_1_outlet_pressure", + "name": "Давление на выходе насоса 1", + "unit": "МПа" + }, + { + "key": "pump_2_outlet_pressure", + "name": "Давление на выходе насоса 2", + "unit": "МПа" + }, + { + "key": "drilling_time_per_meter", + "name": "Время бурения 1м проходки", + "unit": "час." + }, + { + "key": "gas_11_lfl_ch4", + "name": "Загазованность 11 (НКПР) CH4", + "unit": "%" + }, + { + "key": "gas_12_lfl_ch4", + "name": "Загазованность 12 (НКПР) CH4", + "unit": "%" + }, + { + "key": "gas_13_lfl_ch4", + "name": "Загазованность 13 (НКПР) CH4", + "unit": "%" + }, + { + "key": "pump_3_outlet_flow", + "name": "Расход на выходе насоса 3", + "unit": "л/с" + }, + { + "key": "pump_3_outlet_pressure", + "name": "Давление на выходе насоса 3", + "unit": "МПа" + }, + { + "key": "gas_5_h2s", + "name": "Загазованность 5 (H2S)", + "unit": "мг/м3" + }, + { + "key": "gas_6_h2s", + "name": "Загазованность 6 (H2S)", + "unit": "мг/м3" + }, + { + "key": "gas_7_h2s", + "name": "Загазованность 7 (H2S)", + "unit": "мг/м3" + } + ] +} diff --git a/docker-compose.yml b/docker-compose.yml index 7c436be..5c503f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,8 @@ services: HTTP_PORT: "80" WS_PORT: "9090" CLOUD_INGEST_URL: ${CLOUD_INGEST_URL:-https://demo.backend.drill.greact.ru/ingest} + CLOUD_INGEST_API_KEY: ${CLOUD_INGEST_API_KEY:-} + EDGE5_MODBUS_V2_MAPPING_FILE: ${EDGE5_MODBUS_V2_MAPPING_FILE:-/app/src/mappings/edge5-modbus.json} volumes: mosquitto_data: