diff --git a/libs/common/src/constants/power.clamp.enargy.enum.ts b/libs/common/src/constants/power.clamp.enargy.enum.ts new file mode 100644 index 0000000..bfed05b --- /dev/null +++ b/libs/common/src/constants/power.clamp.enargy.enum.ts @@ -0,0 +1,6 @@ +export enum PowerClampEnergyEnum { + ENERGY_CONSUMED = 'EnergyConsumed', + ENERGY_CONSUMED_A = 'EnergyConsumedA', + ENERGY_CONSUMED_B = 'EnergyConsumedB', + ENERGY_CONSUMED_C = 'EnergyConsumedC', +} diff --git a/libs/common/src/constants/sql-query-path.ts b/libs/common/src/constants/sql-query-path.ts index 47417b0..e22c42d 100644 --- a/libs/common/src/constants/sql-query-path.ts +++ b/libs/common/src/constants/sql-query-path.ts @@ -1 +1,2 @@ export const SQL_QUERIES_PATH = 'libs/common/src/sql/queries'; +export const SQL_PROCEDURES_PATH = 'libs/common/src/sql/procedures'; diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index 4f6b94b..739d846 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -19,6 +19,8 @@ import { } from 'firebase/database'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; import { ProductType } from '@app/common/constants/product-type.enum'; +import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; +import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.enum'; @Injectable() export class DeviceStatusFirebaseService { private tuya: TuyaContext; @@ -26,6 +28,7 @@ export class DeviceStatusFirebaseService { constructor( private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, + private readonly powerClampService: PowerClampService, private deviceStatusLogRepository: DeviceStatusLogRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); @@ -75,29 +78,12 @@ export class DeviceStatusFirebaseService { const device = await this.getDeviceByDeviceTuyaUuid( addDeviceStatusDto.deviceTuyaUuid, ); - if (device.productDevice.prodType === ProductType.PC) { - const energyStatus = addDeviceStatusDto.status.find( - (status) => - status.code === 'EnergyConsumed' || - status.code === 'EnergyConsumedA' || - status.code === 'EnergyConsumedB' || - status.code === 'EnergyConsumedC', - ); - - if (energyStatus) { - console.log( - device.productDevice.prodType, - device.uuid, - addDeviceStatusDto.log, - new Date().toLocaleDateString('en-CA'), // Format as YYYY-MM-DD - ); - } - } if (device?.uuid) { return await this.createDeviceStatusFirebase({ deviceUuid: device.uuid, ...addDeviceStatusDto, + productType: device.productDevice.prodType, }); } // Return null if device not found or no UUID @@ -235,6 +221,25 @@ export class DeviceStatusFirebaseService { }); await this.deviceStatusLogRepository.save(newLogs); + if (addDeviceStatusDto.productType === ProductType.PC) { + const energyCodes = new Set([ + PowerClampEnergyEnum.ENERGY_CONSUMED, + PowerClampEnergyEnum.ENERGY_CONSUMED_A, + PowerClampEnergyEnum.ENERGY_CONSUMED_B, + PowerClampEnergyEnum.ENERGY_CONSUMED_C, + ]); + + const energyStatus = addDeviceStatusDto?.log?.properties?.find((status) => + energyCodes.has(status.code), + ); + + if (energyStatus) { + await this.powerClampService.updateEnergyConsumedHistoricalData( + addDeviceStatusDto.deviceUuid, + ); + } + } + // Return the updated data const snapshot: DataSnapshot = await get(dataRef); return snapshot.val(); diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 258a40b..b0e852f 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -1,109 +1,61 @@ import { Injectable } from '@nestjs/common'; -import { - PowerClampDailyRepository, - PowerClampHourlyRepository, - PowerClampMonthlyRepository, -} from '@app/common/modules/power-clamp/repositories'; +import { SqlLoaderService } from './sql-loader.service'; +import { DataSource } from 'typeorm'; +import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path'; @Injectable() export class PowerClampService { constructor( - private readonly powerClampHourlyRepository: PowerClampHourlyRepository, - private readonly powerClampDailyRepository: PowerClampDailyRepository, - private readonly powerClampMonthlyRepository: PowerClampMonthlyRepository, + private readonly sqlLoader: SqlLoaderService, + private readonly dataSource: DataSource, ) {} - async insertOrUpdatePowerClamp( - deviceUuid: string, - logData: any, - ): Promise { + + async updateEnergyConsumedHistoricalData(deviceUuid: string): Promise { try { - await this.insertOrUpdateHourly(deviceUuid, logData); + const now = new Date(); + const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD + const hour = now.getHours(); + const monthYear = now + .toLocaleDateString('en-US', { + month: '2-digit', + year: 'numeric', + }) + .replace('/', '-'); // MM-YYYY + + await this.executeProcedure('fact_hourly_energy_consumed_procedure', [ + deviceUuid, + dateStr, + hour, + ]); + + await this.executeProcedure('fact_daily_energy_consumed_procedure', [ + deviceUuid, + dateStr, + ]); + + await this.executeProcedure('fact_monthly_energy_consumed_procedure', [ + deviceUuid, + monthYear, + ]); } catch (err) { - console.error('Failed to insert or update hourly data', err); + console.error('Failed to insert or update energy data:', err); throw err; } } - async insertOrUpdateHourly( - deviceUuid: string, - logData: LogData, + private async executeProcedure( + procedureFileName: string, + params: (string | number | null)[], ): Promise { - try { - const currentDate = new Date().toLocaleDateString('en-CA'); - const currentHour = new Date().getHours().toString(); + const query = this.loadQuery(procedureFileName); + await this.dataSource.query(query, params); + } - // First try to update existing record - const existingData = await this.powerClampHourlyRepository.findOne({ - where: { - deviceUuid, - date: currentDate, - hour: currentHour, - }, - }); - - if (existingData) { - // Create update object only with values that exist in logData - const updateData: Partial = {}; - - const hasProperty = (code: string) => - logData.properties.some((p) => p.code === code); - const getValue = (code: string) => { - const prop = logData.properties.find((p) => p.code === code); - return prop ? Number(prop.value) : undefined; - }; - - if (hasProperty('EnergyConsumedA')) { - updateData.energyConsumedA = String(getValue('EnergyConsumedA')); - } - if (hasProperty('EnergyConsumedB')) { - updateData.energyConsumedB = String(getValue('EnergyConsumedB')); - } - if (hasProperty('EnergyConsumedC')) { - updateData.energyConsumedC = String(getValue('EnergyConsumedC')); - } - if (hasProperty('EnergyConsumed')) { - updateData.energyConsumedKw = String(getValue('EnergyConsumed')); - } - - if (Object.keys(updateData).length > 0) { - await this.powerClampHourlyRepository.update( - existingData.uuid, - updateData, - ); - } - } else { - // Insert new record with all required fields - const getValue = (code: string) => { - const prop = logData.properties.find((p) => p.code === code); - return prop ? Number(prop.value) : 0; // Default to 0 for required fields - }; - - await this.powerClampHourlyRepository.insert({ - deviceUuid, - date: currentDate, - hour: currentHour, - energyConsumedA: String(getValue('EnergyConsumedA')), - energyConsumedB: String(getValue('EnergyConsumedB')), - energyConsumedC: String(getValue('EnergyConsumedC')), - energyConsumedKw: String(getValue('EnergyConsumed')), - }); - } - } catch (err) { - console.error('Failed to insert or update hourly data', err); - throw err; - } + private loadQuery(fileName: string): string { + return this.sqlLoader.loadQuery( + 'fact_energy_consumed', + fileName, + SQL_PROCEDURES_PATH, + ); } } -interface EnergyProperties { - code: string; - dpId: number; - time: number; - value: string | number; -} - -interface LogData { - devId: string; - dataId: string; - productId: string; - properties: EnergyProperties[]; -} diff --git a/libs/common/src/helper/services/sql-loader.service.ts b/libs/common/src/helper/services/sql-loader.service.ts index cd51dba..6012c42 100644 --- a/libs/common/src/helper/services/sql-loader.service.ts +++ b/libs/common/src/helper/services/sql-loader.service.ts @@ -1,4 +1,3 @@ -import { SQL_QUERIES_PATH } from '@app/common/constants/sql-query-path'; import { Injectable, Logger } from '@nestjs/common'; import { readFileSync } from 'fs'; import { join } from 'path'; @@ -8,13 +7,8 @@ export class SqlLoaderService { private readonly logger = new Logger(SqlLoaderService.name); private readonly sqlRootPath = join(__dirname, '../sql/queries'); - loadQuery(module: string, queryName: string): string { - const filePath = join( - process.cwd(), - SQL_QUERIES_PATH, - module, - `${queryName}.sql`, - ); + loadQuery(module: string, queryName: string, path: string): string { + const filePath = join(process.cwd(), path, module, `${queryName}.sql`); try { return readFileSync(filePath, 'utf8'); } catch (error) { diff --git a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_daily_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_daily_energy_consumed_procedure.sql similarity index 97% rename from libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_daily_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_daily_energy_consumed_procedure.sql index 82725d0..c549891 100644 --- a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_daily_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_energy_consumed/fact_daily_energy_consumed_procedure.sql @@ -1,7 +1,7 @@ WITH params AS ( SELECT - 'd72f3d5d-02e5-4a9e-a1f7-7ab8c3534910'::uuid AS device_id, - '2025-04-23'::date AS target_date + $1::uuid AS device_id, + $2::date AS target_date ), total_energy AS ( SELECT diff --git a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_hourly_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_hourly_energy_consumed_procedure.sql similarity index 96% rename from libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_hourly_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_hourly_energy_consumed_procedure.sql index ff17747..ffefc4f 100644 --- a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_hourly_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_energy_consumed/fact_hourly_energy_consumed_procedure.sql @@ -1,8 +1,8 @@ WITH params AS ( SELECT - 'd72f3d5d-02e5-4a9e-a1f7-7ab8c3534910'::uuid AS device_id, - '2025-04-23'::date AS target_date, - '14'::text AS target_hour + $1::uuid AS device_id, + $2::date AS target_date, + $3::text AS target_hour ), total_energy AS ( SELECT diff --git a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_monthly_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_monthly_energy_consumed_procedure.sql similarity index 96% rename from libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_monthly_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_monthly_energy_consumed_procedure.sql index cdf6134..0e69d60 100644 --- a/libs/common/src/sql/procedures/fact_daily_energy_consumed/fact_monthly_energy_consumed_procedure.sql +++ b/libs/common/src/sql/procedures/fact_energy_consumed/fact_monthly_energy_consumed_procedure.sql @@ -1,7 +1,7 @@ WITH params AS ( SELECT - 'd72f3d5d-02e5-4a9e-a1f7-7ab8c3534910'::uuid AS device_id, - '03-2025'::text AS target_month -- Format should match 'MM-YYYY' + $1::uuid AS device_id, + $2::text AS target_month -- Format should match 'MM-YYYY' ), total_energy AS ( SELECT diff --git a/src/power-clamp/services/power-clamp.service.ts b/src/power-clamp/services/power-clamp.service.ts index bd03f0c..406cb95 100644 --- a/src/power-clamp/services/power-clamp.service.ts +++ b/src/power-clamp/services/power-clamp.service.ts @@ -1,3 +1,4 @@ +import { SQL_QUERIES_PATH } from '@app/common/constants/sql-query-path'; import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; @@ -13,6 +14,7 @@ export class PowerClampService { const sql = this.sqlLoader.loadQuery( 'fact_daily_energy_consumed', 'fact_daily_energy_consumed', + SQL_QUERIES_PATH, ); return this.dataSource.manager.query(sql); } @@ -21,6 +23,7 @@ export class PowerClampService { const sql = this.sqlLoader.loadQuery( 'energy', 'energy_consumed_with_params', + SQL_QUERIES_PATH, ); return this.dataSource.manager.query(sql, [code]); }