Skip to content

Commit 59d52ee

Browse files
committed
Add fixedDefault limits to CSIP-AUS configuration and update related logic
1 parent fe89c81 commit 59d52ee

7 files changed

Lines changed: 97 additions & 26 deletions

File tree

config.schema.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@
2424
"minLength": 10,
2525
"maxLength": 11,
2626
"description": "For in-band registration, the NMI of the site"
27+
},
28+
"fixedDefault": {
29+
"type": "object",
30+
"properties": {
31+
"exportLimitWatts": {
32+
"type": "number",
33+
"minimum": 0,
34+
"description": "The default export limit in watts"
35+
},
36+
"importLimitWatts": {
37+
"type": "number",
38+
"minimum": 0,
39+
"description": "The default import limit in watts"
40+
}
41+
},
42+
"required": [
43+
"exportLimitWatts",
44+
"importLimitWatts"
45+
],
46+
"additionalProperties": false,
47+
"description": "The default limits in case CSIP-AUS server is unreachable and there is no default control"
2748
}
2849
},
2950
"required": [

docs/configuration/setpoints.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ To use the CSIP-AUS provided limits as a setpoint, add following property to `co
3636
"csipAus": {
3737
"host": "https://sep2-test.energyq.com.au", // (string) required: the CSIP-AUS server host
3838
"dcapUri": "/api/v2/dcap", // (string) required: the device capability discovery URI
39-
"nmi": "1234567890" // (string) optional: for utilities that require in-band registration, the NMI of the site
39+
"nmi": "1234567890", // (string) optional: for utilities that require in-band registration, the NMI of the site
40+
"fixedDefault": // (object) optional: the default limits in case CSIP-AUS server is unreachable and there is no default control which may be defined in the connection agreement
41+
{
42+
"exportLimitWatts": 1500, // (number) the default export limit in watts
43+
"importLimitWatts": 1500, // (number) the default import limit in watts
44+
}
4045
}
4146
}
4247
...

src/helpers/config.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ export const configSchema = z.object({
6363
.describe(
6464
'For in-band registration, the NMI of the site',
6565
),
66+
fixedDefault: z
67+
.object({
68+
exportLimitWatts: z
69+
.number()
70+
.min(0)
71+
.describe('The default export limit in watts'),
72+
importLimitWatts: z
73+
.number()
74+
.min(0)
75+
.describe('The default import limit in watts'),
76+
})
77+
.optional()
78+
.describe(
79+
'The default limits in case CSIP-AUS server is unreachable and there is no default control',
80+
),
6681
})
6782
.optional()
6883
.describe('If defined, limit by CSIP-AUS server'),

src/sep2/helpers/controlLimitRamp.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,24 @@ describe('ControlLimitRampHelper', () => {
1919
helper = new ControlLimitRampHelper({ rampRateHelper });
2020
});
2121

22-
it('should return no value for no target', () => {
23-
helper.updateTarget({ type: 'none' });
22+
it('should return no value for no target with undefined', () => {
23+
helper.updateTarget({ type: 'none', value: undefined });
2424

2525
// cache initial value
2626
expect(helper.getRampedValue()).toBe(undefined);
2727

2828
expect(helper.getRampedValue()).toBe(undefined);
2929
});
3030

31+
it('should return a value for no target with a value', () => {
32+
helper.updateTarget({ type: 'none', value: 5000 });
33+
34+
// cache initial value
35+
expect(helper.getRampedValue()).toBe(5000);
36+
37+
expect(helper.getRampedValue()).toBe(5000);
38+
});
39+
3140
it('should return no value for undefined target', () => {
3241
helper.updateTarget({
3342
type: 'active',

src/sep2/helpers/controlLimitRamp.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { cappedChange } from '../../helpers/math.js';
22
import { type RampRateHelper } from './rampRate.js';
33

4+
export type ControlLimitRampTarget =
5+
| {
6+
type: 'active' | 'default';
7+
value: number | undefined;
8+
rampTimeSeconds: number | undefined;
9+
}
10+
| { type: 'none'; value: number | undefined };
11+
412
export class ControlLimitRampHelper {
513
private rampRateHelper: RampRateHelper;
614
private cachedValue:
@@ -10,36 +18,34 @@ export class ControlLimitRampHelper {
1018
time: Date;
1119
isRamping: boolean;
1220
}
13-
| { type: 'none' } = { type: 'none' };
21+
| { type: 'none'; value: number | undefined } = {
22+
type: 'none',
23+
value: undefined,
24+
};
1425
private target:
1526
| {
1627
type: 'active' | 'default';
1728
value: number;
1829
endDateTime: Date | null;
1930
}
20-
| { type: 'none' } = { type: 'none' };
31+
| { type: 'none'; value: number | undefined } = {
32+
type: 'none',
33+
value: undefined,
34+
};
2135

2236
constructor({ rampRateHelper }: { rampRateHelper: RampRateHelper }) {
2337
this.rampRateHelper = rampRateHelper;
2438
}
2539

26-
public updateTarget(
27-
target:
28-
| {
29-
type: 'active' | 'default';
30-
value: number | undefined;
31-
rampTimeSeconds: number | undefined;
32-
}
33-
| { type: 'none' },
34-
) {
40+
public updateTarget(target: ControlLimitRampTarget) {
3541
switch (target.type) {
3642
case 'none':
37-
this.target = { type: 'none' };
43+
this.target = { type: 'none', value: target.value };
3844
return;
3945
case 'active':
4046
case 'default': {
4147
if (target.value === undefined) {
42-
this.target = { type: 'none' };
48+
this.target = { type: 'none', value: target.value };
4349
return;
4450
}
4551

@@ -68,7 +74,7 @@ export class ControlLimitRampHelper {
6874
const value = ((): number | undefined => {
6975
switch (this.target.type) {
7076
case 'none':
71-
return undefined;
77+
return this.target.value;
7278
case 'active':
7379
case 'default': {
7480
switch (this.cachedValue.type) {
@@ -129,7 +135,7 @@ export class ControlLimitRampHelper {
129135

130136
this.cachedValue = (() => {
131137
if (value === undefined || this.target.type === 'none') {
132-
return { type: 'none' };
138+
return { type: 'none', value: this.target.value };
133139
}
134140

135141
const hasReachedTarget = value === this.target.value;

src/sep2/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export function getSep2Instance({
8787
const setpoint = new CsipAusSetpoint({
8888
client: sep2Client,
8989
rampRateHelper,
90+
config,
9091
});
9192

9293
const derControlsHelper = new DerControlsHelper({

src/setpoints/csipAus/index.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { type DerControlsHelperChangedData } from '../../sep2/helpers/derControl
1111
import { type SetpointType } from '../setpoint.js';
1212
import { numberWithPow10 } from '../../helpers/number.js';
1313
import { writeControlLimit } from '../../helpers/influxdb.js';
14+
import { type ControlLimitRampTarget } from '../../sep2/helpers/controlLimitRamp.js';
1415
import { ControlLimitRampHelper } from '../../sep2/helpers/controlLimitRamp.js';
16+
import { type Config } from '../../helpers/config.js';
1517

1618
export class CsipAusSetpoint implements SetpointType {
1719
private schedulerByControlType: {
@@ -22,14 +24,18 @@ export class CsipAusSetpoint implements SetpointType {
2224
private opModImpLimWRampRateHelper: ControlLimitRampHelper;
2325
private opModLoadLimWRampRateHelper: ControlLimitRampHelper;
2426
private logger: Logger;
27+
private config: Config;
2528

2629
constructor({
2730
client,
2831
rampRateHelper,
32+
config,
2933
}: {
3034
client: SEP2Client;
3135
rampRateHelper: RampRateHelper;
36+
config: Config;
3237
}) {
38+
this.config = config;
3339
this.logger = pinoLogger.child({ module: 'InverterController' });
3440

3541
this.schedulerByControlType = {
@@ -91,7 +97,7 @@ export class CsipAusSetpoint implements SetpointType {
9197
this.schedulerByControlType.opModExpLimW.getActiveScheduleDerControlBaseValue();
9298

9399
this.opModExpLimWRampRateHelper.updateTarget(
94-
(() => {
100+
((): ControlLimitRampTarget => {
95101
switch (opModExpLimW.type) {
96102
case 'active':
97103
case 'default': {
@@ -107,7 +113,11 @@ export class CsipAusSetpoint implements SetpointType {
107113
};
108114
}
109115
case 'none':
110-
return { type: 'none' };
116+
return {
117+
type: 'none',
118+
value: this.config.setpoints.csipAus?.fixedDefault
119+
?.exportLimitWatts,
120+
};
111121
}
112122
})(),
113123
);
@@ -116,7 +126,7 @@ export class CsipAusSetpoint implements SetpointType {
116126
this.schedulerByControlType.opModGenLimW.getActiveScheduleDerControlBaseValue();
117127

118128
this.opModGenLimWRampRateHelper.updateTarget(
119-
(() => {
129+
((): ControlLimitRampTarget => {
120130
switch (opModGenLimW.type) {
121131
case 'active':
122132
case 'default': {
@@ -132,7 +142,7 @@ export class CsipAusSetpoint implements SetpointType {
132142
};
133143
}
134144
case 'none':
135-
return { type: 'none' };
145+
return { type: 'none', value: undefined };
136146
}
137147
})(),
138148
);
@@ -141,7 +151,7 @@ export class CsipAusSetpoint implements SetpointType {
141151
this.schedulerByControlType.opModImpLimW.getActiveScheduleDerControlBaseValue();
142152

143153
this.opModImpLimWRampRateHelper.updateTarget(
144-
(() => {
154+
((): ControlLimitRampTarget => {
145155
switch (opModImpLimW.type) {
146156
case 'active':
147157
case 'default': {
@@ -157,7 +167,11 @@ export class CsipAusSetpoint implements SetpointType {
157167
};
158168
}
159169
case 'none':
160-
return { type: 'none' };
170+
return {
171+
type: 'none',
172+
value: this.config.setpoints.csipAus?.fixedDefault
173+
?.importLimitWatts,
174+
};
161175
}
162176
})(),
163177
);
@@ -166,7 +180,7 @@ export class CsipAusSetpoint implements SetpointType {
166180
this.schedulerByControlType.opModLoadLimW.getActiveScheduleDerControlBaseValue();
167181

168182
this.opModLoadLimWRampRateHelper.updateTarget(
169-
(() => {
183+
((): ControlLimitRampTarget => {
170184
switch (opModLoadLimW.type) {
171185
case 'active':
172186
case 'default': {
@@ -182,7 +196,7 @@ export class CsipAusSetpoint implements SetpointType {
182196
};
183197
}
184198
case 'none':
185-
return { type: 'none' };
199+
return { type: 'none', value: undefined };
186200
}
187201
})(),
188202
);

0 commit comments

Comments
 (0)