forked from hank820/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpump-configuration-and-control-server.cpp
370 lines (335 loc) · 14.6 KB
/
pump-configuration-and-control-server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/**
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/util/af.h>
#include <app/util/util.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-enums.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app/ConcreteAttributePath.h>
#include <app/ConcreteCommandPath.h>
#include <app/InteractionModelEngine.h>
#include <app/util/attribute-storage.h>
#include <app/util/config.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::PumpConfigurationAndControl;
namespace chip {
namespace app {
namespace Clusters {
namespace PumpConfigurationAndControl {
// Enum for RemoteSensorType
enum class RemoteSensorType : uint8_t
{
kNoSensor = 0x00,
kPressureSensor = 0x01,
kFlowSensor = 0x02,
kTemperatureSensor = 0x03,
};
static RemoteSensorType detectRemoteSensorConnected()
{
// TODO: Detect the sensor types attached to the pump control cluster
// this could be pressure, flow or temperature sensors.
return RemoteSensorType::kNoSensor;
}
static void setEffectiveModes(EndpointId endpoint)
{
ControlModeEnum controlMode;
OperationModeEnum operationMode;
BitMask<PumpStatusBitmap> pumpStatus;
bool isControlModeAvailable = true;
bool isPumpStatusAvailable = true;
isControlModeAvailable = emberAfContainsAttribute(endpoint, PumpConfigurationAndControl::Id, Attributes::ControlMode::Id);
isPumpStatusAvailable = emberAfContainsAttribute(endpoint, PumpConfigurationAndControl::Id, Attributes::PumpStatus::Id);
// Get the current control- and operation modes
Attributes::OperationMode::Get(endpoint, &operationMode);
if (isControlModeAvailable)
{
Attributes::ControlMode::Get(endpoint, &controlMode);
}
else
{
// If controlMode attribute is not available, then use the default value
// of the effectiveControlMode attribute as the effectiveControlMode
// if this is not suitable, the application should override this value in
// the post attribute change callback for the operation mode attribute
const EmberAfAttributeMetadata * effectiveControlModeMetaData;
effectiveControlModeMetaData = GetAttributeMetadata(
app::ConcreteAttributePath(endpoint, PumpConfigurationAndControl::Id, Attributes::EffectiveControlMode::Id));
controlMode = static_cast<ControlModeEnum>(effectiveControlModeMetaData->defaultValue.defaultValue);
}
if (isPumpStatusAvailable)
{
Attributes::PumpStatus::Get(endpoint, &pumpStatus);
}
switch (operationMode)
{
case OperationModeEnum::kNormal: {
// The pump runs in the control mode as per the type of the remote sensor
// If the remote sensor is a Flow sensor the mode would be ConstantFlow
// If the remote sensor is a Pressure sensor the mode would be ConstantPressure (not ProportionalPressure)
// If the remote sensor is a Temperature sensor the mode would be ConstantTemperature
// If a remote sensor is detected and the OperationMode is kNormal, then the pump is operating in the
// control mode indicated by the repective remote senor type
RemoteSensorType sensorType = detectRemoteSensorConnected();
switch (sensorType)
{
case RemoteSensorType::kFlowSensor:
Attributes::EffectiveControlMode::Set(endpoint, ControlModeEnum::kConstantFlow);
if (isPumpStatusAvailable)
{
pumpStatus.Set(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
break;
case RemoteSensorType::kPressureSensor:
Attributes::EffectiveControlMode::Set(endpoint, ControlModeEnum::kConstantPressure);
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Set(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
break;
case RemoteSensorType::kTemperatureSensor:
Attributes::EffectiveControlMode::Set(endpoint, ControlModeEnum::kConstantTemperature);
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Set(PumpStatusBitmap::kRemoteTemperature);
}
break;
case RemoteSensorType::kNoSensor:
// The pump is controlled by a setpoint, as defined by
// the ControlMode attribute. (N.B. The setpoint is an internal variable which MAY be
// controlled between 0% and 100%, e.g., by means of the Level Control cluster)
// The ControlMode can be any of the following:
// ConstantSpeed, ConstantPressure, ProportionalPressure,
// ConstantFlow, ConstantTemperature or Automatic. The actual ControlMode
// which would be the EffectiveControlMode is dependant on the actual
// physical pump application running "on-top" of this cluster server.
Attributes::EffectiveControlMode::Set(endpoint, controlMode);
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
break;
}
// Set the overall effective operation mode to Normal
Attributes::EffectiveOperationMode::Set(endpoint, OperationModeEnum::kNormal);
}
break;
// The pump is controlled by the OperationMode attribute.
// Maximum, Minimum or Local
case OperationModeEnum::kMaximum: {
#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
uint8_t maxLevel;
#endif
Attributes::EffectiveOperationMode::Set(endpoint, OperationModeEnum::kMaximum);
Attributes::EffectiveControlMode::Set(endpoint, ControlModeEnum::kConstantSpeed);
#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
LevelControl::Attributes::MaxLevel::Get(endpoint, &maxLevel);
LevelControl::Attributes::CurrentLevel::Set(endpoint, maxLevel, false);
#endif
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
}
break;
case OperationModeEnum::kMinimum: {
#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
uint8_t minLevel;
#endif
Attributes::EffectiveOperationMode::Set(endpoint, OperationModeEnum::kMinimum);
Attributes::EffectiveControlMode::Set(endpoint, ControlModeEnum::kConstantSpeed);
#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
LevelControl::Attributes::MinLevel::Get(endpoint, &minLevel);
if (minLevel == 0)
{
// Bump the minimum level to 1, since the value of 0 means stop
minLevel = 1;
}
LevelControl::Attributes::CurrentLevel::Set(endpoint, minLevel, false);
#endif
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
}
break;
case OperationModeEnum::kLocal: {
// If the Application sets the OperatioMode to kLocal the application "owns" the EffectiveControlMode, which
// it also does if the external entity sets the OperationMode to kLocal. So in any case the application
// must set the EffectiveControlMode to something which applies to the current ControlMode in the application.
// So to keeps things short: the application layer owns the EffetiveControlMode when OperationMode is kLocal.
Attributes::EffectiveOperationMode::Set(endpoint, OperationModeEnum::kLocal);
// Set the current ControlMode for now. Perhaps the application will set the EffectiveControlMode to something else.
Attributes::EffectiveControlMode::Set(endpoint, controlMode);
// Clear out the remote sensors from the PumpStatus flags.
if (isPumpStatusAvailable)
{
pumpStatus.Clear(PumpStatusBitmap::kRemoteFlow);
pumpStatus.Clear(PumpStatusBitmap::kRemotePressure);
pumpStatus.Clear(PumpStatusBitmap::kRemoteTemperature);
}
}
break;
case OperationModeEnum::kUnknownEnumValue: {
// Not expected; see check in MatterPumpConfigurationAndControlClusterServerPreAttributeChangedCallback.
break;
}
}
if (isPumpStatusAvailable)
{
Attributes::PumpStatus::Set(endpoint, pumpStatus);
}
}
bool HasFeature(EndpointId endpoint, Feature feature)
{
bool hasFeature;
uint32_t featureMap;
hasFeature = (Attributes::FeatureMap::Get(endpoint, &featureMap) == EMBER_ZCL_STATUS_SUCCESS);
return hasFeature ? ((featureMap & to_underlying(feature)) != 0) : false;
}
} // namespace PumpConfigurationAndControl
} // namespace Clusters
} // namespace app
} // namespace chip
// SDK Callbacks
void emberAfPumpConfigurationAndControlClusterServerInitCallback(EndpointId endpoint)
{
emberAfDebugPrintln("Initialize PCC Server Cluster [EP:%d]", endpoint);
}
chip::Protocols::InteractionModel::Status MatterPumpConfigurationAndControlClusterServerPreAttributeChangedCallback(
const chip::app::ConcreteAttributePath & attributePath, EmberAfAttributeType attributeType, uint16_t size, uint8_t * value)
{
emberAfDebugPrintln("PCC Server Cluster Attribute Pre-changed [EP:%d, ID:0x%x]", attributePath.mEndpointId,
(unsigned int) attributePath.mAttributeId);
Protocols::InteractionModel::Status status = Protocols::InteractionModel::Status::Success;
switch (attributePath.mAttributeId)
{
case Attributes::ControlMode::Id: {
ControlModeEnum controlMode;
NumericAttributeTraits<ControlModeEnum>::StorageType tmp;
memcpy(&tmp, value, size);
controlMode = NumericAttributeTraits<ControlModeEnum>::StorageToWorking(tmp);
switch (controlMode)
{
case ControlModeEnum::kConstantFlow:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantFlow))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kConstantPressure:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantPressure))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kConstantSpeed:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantSpeed))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kConstantTemperature:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantTemperature))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kProportionalPressure:
if (!HasFeature(attributePath.mEndpointId, Feature::kCompensatedPressure))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kAutomatic:
if (!HasFeature(attributePath.mEndpointId, Feature::kAutomatic))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case ControlModeEnum::kUnknownEnumValue:
status = Protocols::InteractionModel::Status::ConstraintError;
break;
}
}
break;
case Attributes::OperationMode::Id:
OperationModeEnum operationMode;
NumericAttributeTraits<OperationModeEnum>::StorageType tmp;
memcpy(&tmp, value, size);
operationMode = NumericAttributeTraits<OperationModeEnum>::StorageToWorking(tmp);
switch (operationMode)
{
case OperationModeEnum::kMinimum:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantSpeed))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case OperationModeEnum::kMaximum:
if (!HasFeature(attributePath.mEndpointId, Feature::kConstantSpeed))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case OperationModeEnum::kLocal:
if (!HasFeature(attributePath.mEndpointId, Feature::kLocalOperation))
{
status = Protocols::InteractionModel::Status::ConstraintError;
}
break;
case OperationModeEnum::kNormal:
status = Protocols::InteractionModel::Status::Success;
break;
case OperationModeEnum::kUnknownEnumValue:
status = Protocols::InteractionModel::Status::ConstraintError;
break;
}
break;
default:
status = Protocols::InteractionModel::Status::Success;
}
return status;
}
void MatterPumpConfigurationAndControlClusterServerAttributeChangedCallback(const app::ConcreteAttributePath & attributePath)
{
emberAfDebugPrintln("PCC Server Cluster Attribute changed [EP:%d, ID:0x%x]", attributePath.mEndpointId,
(unsigned int) attributePath.mAttributeId);
switch (attributePath.mAttributeId)
{
case Attributes::ControlMode::Id:
case Attributes::OperationMode::Id:
setEffectiveModes(attributePath.mEndpointId);
break;
default:
emberAfDebugPrintln("PCC Server: unhandled attribute ID");
}
}
void MatterPumpConfigurationAndControlPluginServerInitCallback() {}