mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 10:25:23 +00:00
feat: enhance PowerClamp service with energy consumption procedures and enums
This commit is contained in:
6
libs/common/src/constants/power.clamp.enargy.enum.ts
Normal file
6
libs/common/src/constants/power.clamp.enargy.enum.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum PowerClampEnergyEnum {
|
||||
ENERGY_CONSUMED = 'EnergyConsumed',
|
||||
ENERGY_CONSUMED_A = 'EnergyConsumedA',
|
||||
ENERGY_CONSUMED_B = 'EnergyConsumedB',
|
||||
ENERGY_CONSUMED_C = 'EnergyConsumedC',
|
||||
}
|
@ -1 +1,2 @@
|
||||
export const SQL_QUERIES_PATH = 'libs/common/src/sql/queries';
|
||||
export const SQL_PROCEDURES_PATH = 'libs/common/src/sql/procedures';
|
||||
|
@ -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<string>('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();
|
||||
|
@ -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<void> {
|
||||
|
||||
async updateEnergyConsumedHistoricalData(deviceUuid: string): Promise<void> {
|
||||
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<void> {
|
||||
try {
|
||||
const currentDate = new Date().toLocaleDateString('en-CA');
|
||||
const currentHour = new Date().getHours().toString();
|
||||
|
||||
// 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<any> = {};
|
||||
|
||||
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'));
|
||||
const query = this.loadQuery(procedureFileName);
|
||||
await this.dataSource.query(query, params);
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await this.powerClampHourlyRepository.update(
|
||||
existingData.uuid,
|
||||
updateData,
|
||||
private loadQuery(fileName: string): string {
|
||||
return this.sqlLoader.loadQuery(
|
||||
'fact_energy_consumed',
|
||||
fileName,
|
||||
SQL_PROCEDURES_PATH,
|
||||
);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
interface EnergyProperties {
|
||||
code: string;
|
||||
dpId: number;
|
||||
time: number;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
interface LogData {
|
||||
devId: string;
|
||||
dataId: string;
|
||||
productId: string;
|
||||
properties: EnergyProperties[];
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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]);
|
||||
}
|
||||
|
Reference in New Issue
Block a user