Add PowerClamp module with controller and service for energy data retrieval

This commit is contained in:
faris Aljohari
2025-04-14 12:17:44 +03:00
parent 5bc69f869d
commit 3745962827
11 changed files with 172 additions and 2 deletions

View File

@ -482,7 +482,15 @@ export class ControllerRoute {
'This endpoint retrieves all devices in a specified group within a space, based on the group name and space UUID.'; 'This endpoint retrieves all devices in a specified group within a space, based on the group name and space UUID.';
}; };
}; };
static PowerClamp = class {
public static readonly ROUTE = 'power-clamp';
static ACTIONS = class {
public static readonly GET_ENERGY_SUMMARY = 'Get power clamp data';
public static readonly GET_ENERGY_DESCRIPTION =
'This endpoint retrieves power clamp data for a specific device.';
};
};
static DEVICE = class { static DEVICE = class {
public static readonly ROUTE = 'devices'; public static readonly ROUTE = 'devices';

View File

@ -0,0 +1 @@
export const SQL_QUERIES_PATH = 'libs/common/src/sql/queries';

View File

@ -0,0 +1,28 @@
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';
@Injectable()
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`,
);
try {
return readFileSync(filePath, 'utf8');
} catch (error) {
this.logger.error(
`Failed to load SQL query: ${module}/${queryName}`,
error.stack,
);
throw new Error(`SQL query not found: ${module}/${queryName}`);
}
}
}

View File

@ -33,7 +33,7 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
}) })
isActive: boolean; isActive: boolean;
@ManyToOne(() => UserEntity, (user) => user.userSpaces, { nullable: false }) @ManyToOne(() => UserEntity, (user) => user.userSpaces, { nullable: true })
user: UserEntity; user: UserEntity;
@OneToMany( @OneToMany(

View File

@ -0,0 +1,65 @@
-- model shows the energy consumed per day per device
WITH total_energy AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='EnergyConsumed'
GROUP BY device_id, date
)
, energy_phase_A AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='EnergyConsumedA'
GROUP BY device_id, date
)
, energy_phase_B AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='EnergyConsumedB'
GROUP BY device_id, date
)
, energy_phase_C AS (
SELECT
device_id,
event_time::date AS date,
MIN(value)::integer AS min_value,
MAX(value)::integer AS max_value
FROM "device-status-log"
where code='EnergyConsumedC'
GROUP BY device_id, date
)
SELECT
total_energy.device_id,
total_energy.date,
(total_energy.max_value-total_energy.min_value) as energy_consumed_kW,
(energy_phase_A.max_value-energy_phase_A.min_value) as energy_consumed_A,
(energy_phase_B.max_value-energy_phase_B.min_value) as energy_consumed_B,
(energy_phase_C.max_value-energy_phase_C.min_value) as energy_consumed_C
FROM total_energy
JOIN energy_phase_A
ON total_energy.device_id=energy_phase_A.device_id
and total_energy.date=energy_phase_A.date
JOIN energy_phase_B
ON total_energy.device_id=energy_phase_B.device_id
and total_energy.date=energy_phase_B.date
JOIN energy_phase_C
ON total_energy.device_id=energy_phase_C.device_id
and total_energy.date=energy_phase_C.date

View File

@ -31,6 +31,7 @@ import { PrivacyPolicyModule } from './privacy-policy/privacy-policy.module';
import { TagModule } from './tags/tags.module'; import { TagModule } from './tags/tags.module';
import { ClientModule } from './client/client.module'; import { ClientModule } from './client/client.module';
import { DeviceCommissionModule } from './commission-device/commission-device.module'; import { DeviceCommissionModule } from './commission-device/commission-device.module';
import { PowerClampModule } from './power-clamp/power-clamp.module';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ ConfigModule.forRoot({
@ -64,8 +65,8 @@ import { DeviceCommissionModule } from './commission-device/commission-device.mo
TermsConditionsModule, TermsConditionsModule,
PrivacyPolicyModule, PrivacyPolicyModule,
TagModule, TagModule,
DeviceCommissionModule, DeviceCommissionModule,
PowerClampModule,
], ],
providers: [ providers: [
{ {

View File

@ -0,0 +1 @@
export * from './power-clamp.controller';

View File

@ -0,0 +1,26 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
import { ControllerRoute } from '@app/common/constants/controller-route';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { PowerClampService } from '../services/power-clamp.service';
@ApiTags('Power Clamp Module')
@Controller({
version: EnableDisableStatusEnum.ENABLED,
path: ControllerRoute.PowerClamp.ROUTE,
})
export class PowerClampController {
constructor(private readonly powerClampService: PowerClampService) {}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get()
@ApiOperation({
summary: ControllerRoute.PowerClamp.ACTIONS.GET_ENERGY_SUMMARY,
description: ControllerRoute.PowerClamp.ACTIONS.GET_ENERGY_DESCRIPTION,
})
async getPowerClampData() {
return await this.powerClampService.getPowerClampData();
}
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { PowerClampService } from './services/power-clamp.service';
import { PowerClampController } from './controllers';
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
@Module({
imports: [ConfigModule],
controllers: [PowerClampController],
providers: [PowerClampService, SqlLoaderService],
exports: [PowerClampService],
})
export class PowerClampModule {}

View File

@ -0,0 +1 @@
export * from './power-clamp.service';

View File

@ -0,0 +1,27 @@
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
@Injectable()
export class PowerClampService {
constructor(
private readonly sqlLoader: SqlLoaderService,
private readonly dataSource: DataSource,
) {}
async getPowerClampData() {
const sql = this.sqlLoader.loadQuery(
'power-clamp',
'fact_daily_energy_consumed',
);
return this.dataSource.manager.query(sql);
}
async getEnergyConsumed(code: string) {
const sql = this.sqlLoader.loadQuery(
'energy',
'energy_consumed_with_params',
);
return this.dataSource.manager.query(sql, [code]);
}
}