Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 :
21 : #include <app/ChunkedWriteCallback.h>
22 : #include <app/InteractionModelEngine.h>
23 : #include <app/WriteClient.h>
24 : #include <controller/CommandSenderAllocator.h>
25 : #include <controller/TypedCommandCallback.h>
26 : #include <functional>
27 : #include <lib/core/Optional.h>
28 :
29 : namespace chip {
30 : namespace Controller {
31 :
32 : namespace Internal {
33 : // WriteCancelFn functions on WriteAttribute() are for internal use only.
34 : typedef std::function<void()> WriteCancelFn;
35 : } // namespace Internal
36 :
37 : /*
38 : * An adapter callback that permits applications to provide std::function callbacks for success, error and on done.
39 : * This permits a slightly more flexible programming model that allows applications to pass in lambdas and bound member functions
40 : * as they see fit instead.
41 : *
42 : */
43 :
44 : class WriteCallback final : public app::WriteClient::Callback
45 : {
46 : public:
47 : using OnSuccessCallbackType = std::function<void(const app::ConcreteAttributePath &)>;
48 :
49 : //
50 : // Callback to deliver any error that occurs during the write. This includes
51 : // errors global to the write as a whole (e.g timeout) as well as per-attribute
52 : // errors.
53 : //
54 : // In the latter case, path will be non-null. Otherwise, it shall be null.
55 : //
56 : using OnErrorCallbackType = std::function<void(const app::ConcreteAttributePath * path, CHIP_ERROR err)>;
57 : using OnDoneCallbackType = std::function<void(app::WriteClient *)>;
58 :
59 7 : WriteCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone, bool aIsGroupWrite) :
60 7 : mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone), mIsGroupWrite(aIsGroupWrite), mCallback(this)
61 7 : {}
62 :
63 7 : app::WriteClient::Callback * GetChunkedCallback() { return &mCallback; }
64 :
65 7 : void OnResponse(const app::WriteClient * apWriteClient, const app::ConcreteDataAttributePath & aPath,
66 : app::StatusIB status) override
67 : {
68 7 : if (mCalledCallback)
69 : {
70 0 : return;
71 : }
72 7 : mCalledCallback = true;
73 :
74 7 : if (status.IsSuccess())
75 : {
76 3 : mOnSuccess(aPath);
77 : }
78 : else
79 : {
80 4 : mOnError(&aPath, status.ToChipError());
81 : }
82 : }
83 :
84 0 : void OnError(const app::WriteClient * apWriteClient, CHIP_ERROR aError) override
85 : {
86 0 : if (mCalledCallback)
87 : {
88 0 : return;
89 : }
90 0 : mCalledCallback = true;
91 :
92 0 : mOnError(nullptr, aError);
93 : }
94 :
95 7 : void OnDone(app::WriteClient * apWriteClient) override
96 : {
97 7 : if (!mIsGroupWrite && !mCalledCallback)
98 : {
99 : // This can happen if the server sends a response with an empty
100 : // WriteResponses list. Since we are not sending wildcard write
101 : // paths, that's not a valid response and we should treat it as an
102 : // error. Use the error we would have gotten if we in fact expected
103 : // a nonempty list.
104 0 : OnError(apWriteClient, CHIP_END_OF_TLV);
105 : }
106 :
107 7 : if (mOnDone != nullptr)
108 : {
109 0 : mOnDone(apWriteClient);
110 : }
111 :
112 7 : chip::Platform::Delete(apWriteClient);
113 : // Always needs to be the last call
114 7 : chip::Platform::Delete(this);
115 7 : }
116 :
117 : private:
118 : OnSuccessCallbackType mOnSuccess = nullptr;
119 : OnErrorCallbackType mOnError = nullptr;
120 : OnDoneCallbackType mOnDone = nullptr;
121 :
122 : bool mCalledCallback = false;
123 : bool mIsGroupWrite = false;
124 :
125 : app::ChunkedWriteCallback mCallback;
126 : };
127 :
128 : /**
129 : * Functions for writing attributes. We have lots of different AttributeInfo
130 : * but a fairly small set of types that get written. So we want to keep the
131 : * template on AttributeInfo very small, and put all the work in the template
132 : * with a small number of instantiations (one per type).
133 : */
134 : template <typename AttrType>
135 7 : CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, ClusterId clusterId,
136 : AttributeId attributeId, const AttrType & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
137 : WriteCallback::OnErrorCallbackType onErrorCb, const Optional<uint16_t> & aTimedWriteTimeoutMs,
138 : WriteCallback::OnDoneCallbackType onDoneCb = nullptr,
139 : const Optional<DataVersion> & aDataVersion = NullOptional,
140 : Internal::WriteCancelFn * outCancelFn = nullptr)
141 : {
142 7 : auto callback = Platform::MakeUnique<WriteCallback>(onSuccessCb, onErrorCb, onDoneCb, sessionHandle->IsGroupSession());
143 7 : VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
144 :
145 7 : auto client = Platform::MakeUnique<app::WriteClient>(app::InteractionModelEngine::GetInstance()->GetExchangeManager(),
146 7 : callback->GetChunkedCallback(), aTimedWriteTimeoutMs);
147 7 : VerifyOrReturnError(client != nullptr, CHIP_ERROR_NO_MEMORY);
148 :
149 7 : if (sessionHandle->IsGroupSession())
150 : {
151 0 : ReturnErrorOnFailure(client->EncodeAttribute(chip::app::AttributePathParams(clusterId, attributeId), requestData));
152 : }
153 : else
154 : {
155 7 : ReturnErrorOnFailure(
156 : client->EncodeAttribute(chip::app::AttributePathParams(endpointId, clusterId, attributeId), requestData, aDataVersion));
157 : }
158 :
159 7 : ReturnErrorOnFailure(client->SendWriteRequest(sessionHandle));
160 :
161 : // If requested by the caller, provide a way to cancel the write interaction.
162 7 : if (outCancelFn != nullptr)
163 : {
164 0 : *outCancelFn = [rawCallback = callback.get(), rawClient = client.get()]() {
165 0 : chip::Platform::Delete(rawClient);
166 0 : chip::Platform::Delete(rawCallback);
167 : };
168 : }
169 :
170 : // At this point the handle will ensure our callback's OnDone is always
171 : // called.
172 7 : client.release();
173 7 : callback.release();
174 :
175 7 : return CHIP_NO_ERROR;
176 7 : }
177 :
178 : template <typename AttributeInfo>
179 7 : CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId,
180 : const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
181 : WriteCallback::OnErrorCallbackType onErrorCb, const Optional<uint16_t> & aTimedWriteTimeoutMs,
182 : WriteCallback::OnDoneCallbackType onDoneCb = nullptr,
183 : const Optional<DataVersion> & aDataVersion = NullOptional)
184 : {
185 14 : return WriteAttribute(sessionHandle, endpointId, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), requestData,
186 7 : onSuccessCb, onErrorCb, aTimedWriteTimeoutMs, onDoneCb, aDataVersion);
187 : }
188 :
189 : template <typename AttributeInfo>
190 : CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId,
191 : const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
192 : WriteCallback::OnErrorCallbackType onErrorCb, uint16_t aTimedWriteTimeoutMs,
193 : WriteCallback::OnDoneCallbackType onDoneCb = nullptr,
194 : const Optional<DataVersion> & aDataVersion = NullOptional)
195 : {
196 : return WriteAttribute<AttributeInfo>(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, onDoneCb,
197 : MakeOptional(aTimedWriteTimeoutMs), onDoneCb, aDataVersion);
198 : }
199 :
200 : template <typename AttributeInfo, typename std::enable_if_t<!AttributeInfo::MustUseTimedWrite(), int> = 0>
201 7 : CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId,
202 : const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
203 : WriteCallback::OnErrorCallbackType onErrorCb, WriteCallback::OnDoneCallbackType onDoneCb = nullptr,
204 : const Optional<DataVersion> & aDataVersion = NullOptional)
205 : {
206 14 : return WriteAttribute<AttributeInfo>(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, NullOptional, onDoneCb,
207 7 : aDataVersion);
208 : }
209 :
210 : } // namespace Controller
211 : } // namespace chip
|