Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/WebApi_ws_live.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, st
addField(chanTypeObj, inv, t, c, FLD_PAC);
addField(chanTypeObj, inv, t, c, FLD_UAC);
addField(chanTypeObj, inv, t, c, FLD_IAC);
// Add HMT-specific 3-phase fields if available
if (t == TYPE_AC) {
addField(chanTypeObj, inv, t, c, FLD_UAC_1N);
addField(chanTypeObj, inv, t, c, FLD_UAC_2N);
addField(chanTypeObj, inv, t, c, FLD_UAC_3N);
addField(chanTypeObj, inv, t, c, FLD_IAC_1);
addField(chanTypeObj, inv, t, c, FLD_IAC_2);
addField(chanTypeObj, inv, t, c, FLD_IAC_3);
}
if (t == TYPE_INV) {
addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC");
} else {
Expand Down
12 changes: 9 additions & 3 deletions webapp/src/components/InverterChannelInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@
</div>

<div v-if="channelType == 'DC'" class="card-header">
<template v-if="channelData.name.u != ''">{{ channelData.name.u }}</template>
<template v-if="channelData.name && channelData.name.u != ''">{{ channelData.name.u }}</template>
<template v-else>{{ $t('inverterchannelinfo.String', { num: channelNumber + 1 }) }}</template>
</div>

<div v-if="channelType == 'AC'" class="card-header text-bg-info">
{{ $t('inverterchannelinfo.Phase', { num: channelNumber + 1 }) }}
<template v-if="phaseNumber !== undefined">
{{ $t('inverterchannelinfo.Phase', { num: phaseNumber }) }}
</template>
<template v-else>
{{ $t('inverterchannelinfo.AC') }}
</template>
</div>

<div class="table-responsive">
Expand Down Expand Up @@ -48,9 +53,10 @@ import { defineComponent, type PropType } from 'vue';

export default defineComponent({
props: {
channelData: { type: Object as PropType<InverterStatistics>, required: true },
channelData: { type: Object as PropType<Partial<InverterStatistics>>, required: true },
channelType: { type: String, required: true },
channelNumber: { type: Number, required: true },
phaseNumber: { type: Number, required: false },
},
});
</script>
1 change: 1 addition & 0 deletions webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@
"inverterchannelinfo": {
"String": "String {num}",
"Phase": "Phase {num}",
"AC": "AC",
"General": "General"
},
"invertertotalinfo": {
Expand Down
7 changes: 7 additions & 0 deletions webapp/src/types/LiveDataStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export interface InverterStatistics {
ReactivePower?: ValueObject;
Efficiency?: ValueObject;
Irradiation?: ValueObject;
// 3-phase fields for HMT inverters
'Voltage Ph1-N'?: ValueObject;
'Voltage Ph2-N'?: ValueObject;
'Voltage Ph3-N'?: ValueObject;
'Current Ph1'?: ValueObject;
'Current Ph2'?: ValueObject;
'Current Ph3'?: ValueObject;
}

export interface RadioStatistics {
Expand Down
115 changes: 99 additions & 16 deletions webapp/src/views/HomeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,22 +190,15 @@
].reverse()"
>
<template v-if="chanType.obj != null">
<template
v-for="channel in Object.keys(chanType.obj)
.sort()
.reverse()
.map((x) => +x)"
:key="channel"
>
<!-- Handle AC data -->
<template v-if="chanType.name == 'AC'">
<!-- Always show AC summary section for all inverters -->
<template
v-if="
chanType.name != 'DC' ||
(chanType.name == 'DC' && getSumIrridiation(inverter) == 0) ||
(chanType.name == 'DC' &&
getSumIrridiation(inverter) > 0 &&
chanType.obj[channel]?.Irradiation?.max) ||
0 > 0
"
v-for="channel in Object.keys(chanType.obj)
.sort()
.reverse()
.map((x) => +x)"
:key="`${chanType.name}-summary-${channel}`"
>
<div class="col" v-if="chanType.obj[channel]">
<InverterChannelInfo
Expand All @@ -215,6 +208,52 @@
/>
</div>
</template>

<!-- Show individual phase sections for 3-phase inverters -->
<template v-if="isThreePhaseInverter(inverter) && inverter.AC[0]">
<div
v-for="phase in [3, 2, 1]"
:key="`${chanType.name}-phase-${phase}`"
class="col"
>
<InverterChannelInfo
:channelData="getPhaseDataWithPower(inverter.AC[0], phase)"
:channelType="chanType.name"
:channelNumber="0"
:phaseNumber="phase"
/>
</div>
</template>
</template>

<!-- Handle non-AC channel types normally -->
<template v-else>
<template
v-for="channel in Object.keys(chanType.obj)
.sort()
.reverse()
.map((x) => +x)"
:key="channel"
>
<template
v-if="
chanType.name != 'DC' ||
(chanType.name == 'DC' && getSumIrridiation(inverter) == 0) ||
(chanType.name == 'DC' &&
getSumIrridiation(inverter) > 0 &&
chanType.obj[channel]?.Irradiation?.max) ||
0 > 0
"
>
<div class="col" v-if="chanType.obj[channel]">
<InverterChannelInfo
:channelData="chanType.obj[channel]"
:channelType="chanType.name"
:channelNumber="channel"
/>
</div>
</template>
</template>
</template>
</template>
</template>
Expand Down Expand Up @@ -528,7 +567,7 @@ import type { GridProfileRawdata } from '@/types/GridProfileRawdata';
import type { GridProfileStatus } from '@/types/GridProfileStatus';
import type { LimitConfig } from '@/types/LimitConfig';
import type { LimitStatus } from '@/types/LimitStatus';
import type { Inverter, LiveData } from '@/types/LiveDataStatus';
import type { Inverter, LiveData, InverterStatistics } from '@/types/LiveDataStatus';
import { authHeader, authUrl, handleResponse, isLoggedIn } from '@/utils/authentication';
import * as bootstrap from 'bootstrap';
import {
Expand Down Expand Up @@ -992,6 +1031,50 @@ export default defineComponent({
}
return this.$n(val_small / val_large, 'percent');
},
isThreePhaseInverter(inverter: Inverter): boolean {
// Check if the inverter has 3-phase specific fields
if (!inverter.AC || !inverter.AC[0]) {
return false;
}

const acData = inverter.AC[0];
// Look for 3-phase specific field names
const hasPhase1Voltage = !!acData['Voltage Ph1-N'];
const hasPhase2Voltage = !!acData['Voltage Ph2-N'];
const hasPhase3Voltage = !!acData['Voltage Ph3-N'];

return hasPhase1Voltage && hasPhase2Voltage && hasPhase3Voltage;
},
getPhaseDataWithPower(acData: InverterStatistics, phase: number): Partial<InverterStatistics> {
// Extract data for a specific phase from the AC data with calculated power
const phaseData: Partial<InverterStatistics> = {};

// Copy phase-specific voltage field
const voltageField = `Voltage Ph${phase}-N` as keyof InverterStatistics;
if (acData[voltageField]) {
phaseData.Voltage = acData[voltageField];
}

// Copy phase-specific current field
const currentField = `Current Ph${phase}` as keyof InverterStatistics;
if (acData[currentField]) {
phaseData.Current = acData[currentField];
}

// Calculate power for this phase: P = V * I * PowerFactor
if (phaseData.Voltage && phaseData.Current) {
const powerFactor = acData.PowerFactor?.v ?? 1; // Default to 1 if not available

phaseData.Power = {
v: phaseData.Voltage.v * phaseData.Current.v * powerFactor,
u: 'W',
d: 2,
max: 0,
};
}

return phaseData;
},
},
});
</script>
Expand Down