blob: 8169e3eeb4c7271d9de9284182f7ab3370134498 [file] [log] [blame]
Constantin Ziesche857c7ab2020-02-25 11:24:51 +01001/*******************************************************************************
2* Copyright (c) 2020 Robert Bosch GmbH
3* Author: Constantin Ziesche (constantin.ziesche@bosch.com)
4*
5* This program and the accompanying materials are made available under the
6* terms of the Eclipse Public License 2.0 which is available at
7* http://www.eclipse.org/legal/epl-2.0
8*
9* SPDX-License-Identifier: EPL-2.0
10*******************************************************************************/
11using BaSyx.API.AssetAdministrationShell;
12using BaSyx.Models.Core.AssetAdministrationShell.Generics;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010013using BaSyx.Utils.ResultHandling;
14using BaSyx.Utils.Client;
15using System.Collections.Generic;
16using System;
17using Newtonsoft.Json;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010018using System.Linq;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010019using BaSyx.Models.Connectivity.Descriptors;
20using BaSyx.Models.Core.Common;
Constantin Zieschefa612082020-04-03 09:54:56 +020021using BaSyx.Models.Communication;
22using System.Threading.Tasks;
Constantin Ziesche08215502020-09-21 19:08:32 +020023using NLog;
24using System.Threading;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010025
26namespace BaSyx.API.Components
27{
Constantin Ziesche08215502020-09-21 19:08:32 +020028 /// <summary>
29 /// Reference implementation of ISubmodelServiceProvider interface
30 /// </summary>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010031 public class SubmodelServiceProvider : ISubmodelServiceProvider
32 {
Constantin Ziesche08215502020-09-21 19:08:32 +020033 private static readonly ILogger logger = LogManager.GetCurrentClassLogger();
34
35 private ISubmodel _submodel;
36
Constantin Ziescheb28a71c2020-10-13 08:39:42 +020037 public const int DEFAULT_TIMEOUT = 30000;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010038 public ISubmodelDescriptor ServiceDescriptor { get; internal set; }
39
Constantin Ziesche08215502020-09-21 19:08:32 +020040 private readonly Dictionary<string, MethodCalledHandler> methodCalledHandler;
41 private readonly Dictionary<string, SubmodelElementHandler> submodelElementHandler;
Constantin Zieschee837f992020-08-19 12:04:32 +020042 private readonly Dictionary<string, Action<IValue>> updateFunctions;
43 private readonly Dictionary<string, EventDelegate> eventDelegates;
44 private readonly Dictionary<string, InvocationResponse> invocationResults;
Constantin Ziescheb28a71c2020-10-13 08:39:42 +020045
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010046 private IMessageClient messageClient;
47
Constantin Ziesche08215502020-09-21 19:08:32 +020048 /// <summary>
49 /// Constructor for SubmodelServiceProvider
50 /// </summary>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010051 public SubmodelServiceProvider()
52 {
Constantin Ziesche08215502020-09-21 19:08:32 +020053 methodCalledHandler = new Dictionary<string, MethodCalledHandler>();
54 submodelElementHandler = new Dictionary<string, SubmodelElementHandler>();
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010055 updateFunctions = new Dictionary<string, Action<IValue>>();
56 eventDelegates = new Dictionary<string, EventDelegate>();
Constantin Zieschefa612082020-04-03 09:54:56 +020057 invocationResults = new Dictionary<string, InvocationResponse>();
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010058 }
Constantin Ziesche08215502020-09-21 19:08:32 +020059 /// <summary>
60 /// Contructor for SubmodelServiceProvider with a Submodel object to bind to
61 /// </summary>
62 /// <param name="submodel">Submodel object</param>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010063 public SubmodelServiceProvider(ISubmodel submodel) : this()
64 {
65 BindTo(submodel);
66 }
Constantin Ziesche08215502020-09-21 19:08:32 +020067 /// <summary>
68 /// Contructor for SubmodelServiceProvider with a Submodel object to bind to and a SubmodelDescriptor as ServiceDescriptor
69 /// </summary>
70 /// <param name="submodel">Submodel object</param>
71 /// <param name="submodelDescriptor">SubmodelDescriptor object</param>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010072 public SubmodelServiceProvider(ISubmodel submodel, ISubmodelDescriptor submodelDescriptor) : this()
73 {
Constantin Ziesche08215502020-09-21 19:08:32 +020074 _submodel = submodel;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010075 ServiceDescriptor = submodelDescriptor;
76 }
77
Constantin Ziesche08215502020-09-21 19:08:32 +020078 /// <summary>
79 /// Bind this SubmodelServiceProvider to a specific Submodel
80 /// </summary>
81 /// <param name="submodel">Submodel object</param>
82 public void BindTo(ISubmodel submodel)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010083 {
Constantin Ziesche08215502020-09-21 19:08:32 +020084 _submodel = submodel;
85 ServiceDescriptor = new SubmodelDescriptor(submodel, null);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010086 }
Constantin Ziesche08215502020-09-21 19:08:32 +020087
88 /// <summary>
89 /// Returns the model binding of this SubmodelServiceProvider
90 /// </summary>
91 /// <returns>Submodel object</returns>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010092 public ISubmodel GetBinding()
93 {
Constantin Ziesche08215502020-09-21 19:08:32 +020094 return _submodel;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010095 }
96
Constantin Ziesche08215502020-09-21 19:08:32 +020097 public void UseInMemorySubmodelElementHandler()
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010098 {
Constantin Ziesche08215502020-09-21 19:08:32 +020099 UseInMemorySubmodelElementHandlerInternal(_submodel.SubmodelElements);
100 }
101 private void UseInMemorySubmodelElementHandlerInternal(IElementContainer<ISubmodelElement> submodelElements)
102 {
103 if (submodelElements.HasChildren())
104 {
105 foreach (var child in submodelElements.Children)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100106 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200107 UseInMemorySubmodelElementHandlerInternal(child);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100108 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200109 }
110 if(submodelElements.Value != null)
111 {
112 if (submodelElements.Value.ModelType != ModelType.Operation)
113 RegisterSubmodelElementHandler(submodelElements.Path, new SubmodelElementHandler(submodelElements.Value.Get, submodelElements.Value.Set));
114 }
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100115 }
116
Constantin Ziesche08215502020-09-21 19:08:32 +0200117 /// <summary>
118 /// Use as specific SubmodelElementHandler for all SubmodelElements
119 /// </summary>
120 /// <param name="elementHandler">SubmodelElementHandler</param>
121 public void UseSubmodelElementHandler(SubmodelElementHandler elementHandler)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100122 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200123 UseSubmodelElementHandlerInternal(_submodel.SubmodelElements, elementHandler, null);
124 }
125 /// <summary>
126 /// Use a specific SubmodelElementHandler for all SubmodelElements of a specific ModelType (e.g. Property) except Operations
127 /// </summary>
128 /// <param name="elementHandler">SubmodelElementHandler</param>
129 /// <param name="modelType">ModelType</param>
130 public void UseSubmodelElementHandlerForModelType(SubmodelElementHandler elementHandler, ModelType modelType)
131 {
132 UseSubmodelElementHandlerInternal(_submodel.SubmodelElements, elementHandler, modelType);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100133 }
134
Constantin Ziesche08215502020-09-21 19:08:32 +0200135 private void UseSubmodelElementHandlerInternal(IElementContainer<ISubmodelElement> submodelElements, SubmodelElementHandler elementHandler, ModelType modelType = null)
136 {
137 if (submodelElements.HasChildren())
138 {
139 foreach (var child in submodelElements.Children)
140 {
141 UseSubmodelElementHandlerInternal(child, elementHandler, modelType);
142 }
143 }
144 if (submodelElements.Value != null)
145 {
146 if (modelType == null)
147 RegisterSubmodelElementHandler(submodelElements.Path, elementHandler);
148 else if (submodelElements.Value.ModelType == modelType)
149 RegisterSubmodelElementHandler(submodelElements.Path, elementHandler);
150 else
151 return;
152 }
153 }
154 /// <summary>
155 /// Use a specific MethodCalledHandler for all Operations
156 /// </summary>
157 /// <param name="methodCalledHandler"></param>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100158 public void UseOperationHandler(MethodCalledHandler methodCalledHandler)
159 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200160 UseOperationHandlerInternal(_submodel.SubmodelElements, methodCalledHandler);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100161 }
162
Constantin Ziesche08215502020-09-21 19:08:32 +0200163 private void UseOperationHandlerInternal(IElementContainer<ISubmodelElement> submodelElements, MethodCalledHandler methodCalledHandler)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100164 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200165 if (submodelElements.HasChildren())
166 {
167 foreach (var child in submodelElements.Children)
168 {
169 UseOperationHandlerInternal(child, methodCalledHandler);
170 }
171 }
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100172 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200173 {
174 if (submodelElements.Value is IOperation)
175 RegisterMethodCalledHandler(submodelElements.Path, methodCalledHandler);
176 else
177 return;
178 }
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100179 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200180
181 public MethodCalledHandler RetrieveMethodCalledHandler(string pathToOperation)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100182 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200183 if (methodCalledHandler.TryGetValue(pathToOperation, out MethodCalledHandler handler))
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100184 return handler;
185 else
186 return null;
187 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200188
189 public SubmodelElementHandler RetrieveSubmodelElementHandler(string pathToElement)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100190 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200191 if (submodelElementHandler.TryGetValue(pathToElement, out SubmodelElementHandler elementHandler))
192 return elementHandler;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100193 else
194 return null;
195 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200196
197 public void RegisterSubmodelElementHandler(string pathToElement, SubmodelElementHandler elementHandler)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100198 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200199 if (!submodelElementHandler.ContainsKey(pathToElement))
200 submodelElementHandler.Add(pathToElement, elementHandler);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100201 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200202 submodelElementHandler[pathToElement] = elementHandler;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100203 }
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200204
205 public void UnregisterSubmodelElementHandler(string pathToElement)
206 {
207 if (submodelElementHandler.ContainsKey(pathToElement))
208 submodelElementHandler.Remove(pathToElement);
209 }
210
Constantin Ziesche08215502020-09-21 19:08:32 +0200211 public void RegisterMethodCalledHandler(string pathToOperation, MethodCalledHandler handler)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100212 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200213 if (!methodCalledHandler.ContainsKey(pathToOperation))
214 methodCalledHandler.Add(pathToOperation, handler);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100215 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200216 methodCalledHandler[pathToOperation] = handler;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100217 }
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200218
Constantin Ziesche08215502020-09-21 19:08:32 +0200219 public void RegisterEventDelegate(string pathToEvent, EventDelegate eventDelegate)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100220 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200221 if (!eventDelegates.ContainsKey(pathToEvent))
222 eventDelegates.Add(pathToEvent, eventDelegate);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100223 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200224 eventDelegates[pathToEvent] = eventDelegate;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100225 }
226
Constantin Ziesche08215502020-09-21 19:08:32 +0200227 public IResult<InvocationResponse> InvokeOperation(string pathToOperation, InvocationRequest invocationRequest)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100228 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200229 if (_submodel == null)
230 return new Result<InvocationResponse>(false, new NotFoundMessage("Submodel"));
231
232 var operation_Retrieved = _submodel.SubmodelElements.Retrieve<IOperation>(pathToOperation);
233 if (operation_Retrieved.Success && operation_Retrieved.Entity != null)
234 {
235 MethodCalledHandler methodHandler;
236 if (operation_Retrieved.Entity.OnMethodCalled != null)
237 methodHandler = operation_Retrieved.Entity.OnMethodCalled;
238 else if (methodCalledHandler.TryGetValue(pathToOperation, out MethodCalledHandler handler))
239 methodHandler = handler;
240 else
241 return new Result<InvocationResponse>(false, new NotFoundMessage($"MethodHandler for {pathToOperation}"));
242
243 InvocationResponse invocationResponse = new InvocationResponse(invocationRequest.RequestId);
244 invocationResponse.InOutputArguments = invocationRequest.InOutputArguments;
245
Constantin Ziescheb28a71c2020-10-13 08:39:42 +0200246 int timeout = DEFAULT_TIMEOUT;
247 if (invocationRequest.Timeout.HasValue)
248 timeout = invocationRequest.Timeout.Value;
249
Constantin Ziesche08215502020-09-21 19:08:32 +0200250 using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
251 {
252 Task<OperationResult> runner = Task.Run(async () =>
253 {
254 try
255 {
256 invocationResponse.ExecutionState = ExecutionState.Running;
257 var result = await methodHandler.Invoke(operation_Retrieved.Entity, invocationRequest.InputArguments, invocationResponse.InOutputArguments, invocationResponse.OutputArguments, cancellationTokenSource.Token);
258 invocationResponse.ExecutionState = ExecutionState.Completed;
259 return result;
260 }
261 catch (Exception e)
262 {
263 invocationResponse.ExecutionState = ExecutionState.Failed;
264 return new OperationResult(e);
265 }
266
267 }, cancellationTokenSource.Token);
268
Constantin Ziescheb28a71c2020-10-13 08:39:42 +0200269 if (Task.WhenAny(runner, Task.Delay(timeout, cancellationTokenSource.Token)).Result == runner)
Constantin Ziesche08215502020-09-21 19:08:32 +0200270 {
271 cancellationTokenSource.Cancel();
272 invocationResponse.OperationResult = runner.Result;
273 return new Result<InvocationResponse>(true, invocationResponse);
274 }
275 else
276 {
277 cancellationTokenSource.Cancel();
278 invocationResponse.OperationResult = new OperationResult(false, new TimeoutMessage());
279 invocationResponse.ExecutionState = ExecutionState.Timeout;
280 return new Result<InvocationResponse>(false, invocationResponse);
281 }
282 }
283 }
284 return new Result<InvocationResponse>(operation_Retrieved);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100285 }
286
Constantin Ziesche08215502020-09-21 19:08:32 +0200287 public IResult<CallbackResponse> InvokeOperationAsync(string pathToOperation, InvocationRequest invocationRequest)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100288 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200289 if (_submodel == null)
Constantin Zieschefa612082020-04-03 09:54:56 +0200290 return new Result<CallbackResponse>(false, new NotFoundMessage("Submodel"));
291 if (invocationRequest == null)
292 return new Result<CallbackResponse>(new ArgumentNullException(nameof(invocationRequest)));
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100293
Constantin Ziesche08215502020-09-21 19:08:32 +0200294 var operation_Retrieved = _submodel.SubmodelElements.Retrieve<IOperation>(pathToOperation);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100295 if (operation_Retrieved.Success && operation_Retrieved.Entity != null)
296 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200297 MethodCalledHandler methodHandler;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100298 if (operation_Retrieved.Entity.OnMethodCalled != null)
Constantin Ziesche08215502020-09-21 19:08:32 +0200299 methodHandler = operation_Retrieved.Entity.OnMethodCalled;
300 else if (methodCalledHandler.TryGetValue(pathToOperation, out MethodCalledHandler handler))
301 methodHandler = handler;
Constantin Zieschefa612082020-04-03 09:54:56 +0200302 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200303 return new Result<CallbackResponse>(false, new NotFoundMessage($"MethodHandler for {pathToOperation}"));
Constantin Ziescheb28a71c2020-10-13 08:39:42 +0200304
Constantin Ziesche08215502020-09-21 19:08:32 +0200305 Task invocationTask = Task.Run(async() =>
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100306 {
Constantin Zieschefa612082020-04-03 09:54:56 +0200307 InvocationResponse invocationResponse = new InvocationResponse(invocationRequest.RequestId);
Constantin Ziesche08215502020-09-21 19:08:32 +0200308 invocationResponse.InOutputArguments = invocationRequest.InOutputArguments;
309 SetInvocationResult(pathToOperation, invocationRequest.RequestId, ref invocationResponse);
Constantin Zieschefa612082020-04-03 09:54:56 +0200310
Constantin Ziescheb28a71c2020-10-13 08:39:42 +0200311 int timeout = DEFAULT_TIMEOUT;
312 if (invocationRequest.Timeout.HasValue)
313 timeout = invocationRequest.Timeout.Value;
314
Constantin Ziesche08215502020-09-21 19:08:32 +0200315 using (var cancellationTokenSource = new CancellationTokenSource())
Constantin Zieschefa612082020-04-03 09:54:56 +0200316 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200317 Task<OperationResult> runner = Task.Run(async () =>
318 {
319 try
320 {
321 invocationResponse.ExecutionState = ExecutionState.Running;
322 var result = await methodHandler.Invoke(operation_Retrieved.Entity, invocationRequest.InputArguments, invocationResponse.InOutputArguments, invocationResponse.OutputArguments, cancellationTokenSource.Token);
323 invocationResponse.ExecutionState = ExecutionState.Completed;
324 return result;
325 }
326 catch (Exception e)
327 {
328 invocationResponse.ExecutionState = ExecutionState.Failed;
329 return new OperationResult(e);
330 }
331 }, cancellationTokenSource.Token);
332
Constantin Ziescheb28a71c2020-10-13 08:39:42 +0200333 if (await Task.WhenAny(runner, Task.Delay(timeout, cancellationTokenSource.Token)) == runner)
Constantin Ziesche08215502020-09-21 19:08:32 +0200334 {
335 cancellationTokenSource.Cancel();
336 invocationResponse.OperationResult = runner.Result;
337 }
338 else
339 {
340 cancellationTokenSource.Cancel();
341 invocationResponse.OperationResult = new OperationResult(false, new TimeoutMessage());
342 invocationResponse.ExecutionState = ExecutionState.Timeout;
343 }
Constantin Zieschefa612082020-04-03 09:54:56 +0200344 }
345 });
Constantin Ziesche08215502020-09-21 19:08:32 +0200346
Constantin Zieschefa612082020-04-03 09:54:56 +0200347 string endpoint = ServiceDescriptor?.Endpoints?.FirstOrDefault()?.Address;
348 CallbackResponse callbackResponse = new CallbackResponse(invocationRequest.RequestId);
349 if (string.IsNullOrEmpty(endpoint))
Constantin Ziesche08215502020-09-21 19:08:32 +0200350 callbackResponse.CallbackUrl = new Uri($"/submodelElements/{pathToOperation}/invocationList/{invocationRequest.RequestId}", UriKind.Relative);
Constantin Zieschefa612082020-04-03 09:54:56 +0200351 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200352 callbackResponse.CallbackUrl = new Uri($"{endpoint}/submodelElements/{pathToOperation}/invocationList/{invocationRequest.RequestId}", UriKind.Absolute);
Constantin Zieschefa612082020-04-03 09:54:56 +0200353 return new Result<CallbackResponse>(true, callbackResponse);
354 }
355 return new Result<CallbackResponse>(operation_Retrieved);
356 }
357
358 private void SetInvocationResult(string operationId, string requestId, ref InvocationResponse invocationResponse)
359 {
360 string key = string.Join("_", operationId, requestId);
361 if (invocationResults.ContainsKey(key))
362 {
363 invocationResults[key] = invocationResponse;
364 }
365 else
366 {
367 invocationResults.Add(key, invocationResponse);
368 }
369 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200370
371 public IResult<InvocationResponse> GetInvocationResult(string pathToOperation, string requestId)
Constantin Zieschefa612082020-04-03 09:54:56 +0200372 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200373 string key = string.Join("_", pathToOperation, requestId);
Constantin Zieschefa612082020-04-03 09:54:56 +0200374 if (invocationResults.ContainsKey(key))
375 {
376 return new Result<InvocationResponse>(true, invocationResults[key]);
377 }
378 else
379 {
380 return new Result<InvocationResponse>(false, new NotFoundMessage($"Request with id {requestId}"));
381 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200382 }
383
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100384 public IResult ThrowEvent(IPublishableEvent publishableEvent, string topic = "/", Action<IMessagePublishedEventArgs> MessagePublished = null, byte qosLevel = 2, bool retain = false)
385 {
386 if (messageClient == null || !messageClient.IsConnected)
387 return new Result(false, new Message(MessageType.Warning, "MessageClient is not initialized or not connected"));
388
389 if (publishableEvent == null)
Constantin Zieschee837f992020-08-19 12:04:32 +0200390 return new Result(new ArgumentNullException(nameof(publishableEvent)));
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100391
392 if (eventDelegates.TryGetValue(publishableEvent.Name, out EventDelegate eventDelegate))
393 eventDelegate.Invoke(this, publishableEvent);
394
Constantin Zieschefa612082020-04-03 09:54:56 +0200395 string message = JsonConvert.SerializeObject(publishableEvent, Formatting.Indented);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100396 return messageClient.Publish(topic, message, MessagePublished, qosLevel, retain);
397 }
398
Constantin Ziesche08215502020-09-21 19:08:32 +0200399
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100400 public virtual void ConfigureEventHandler(IMessageClient messageClient)
401 {
402 this.messageClient = messageClient;
403 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200404
405 public virtual void SubscribeUpdates(string pathToSubmodelElement, Action<IValue> updateFunction)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100406 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200407 if (!updateFunctions.ContainsKey(pathToSubmodelElement))
408 updateFunctions.Add(pathToSubmodelElement, updateFunction);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100409 else
Constantin Ziesche08215502020-09-21 19:08:32 +0200410 updateFunctions[pathToSubmodelElement] = updateFunction;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100411 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200412
413 public virtual void PublishUpdate(string pathToSubmodelElement, IValue value)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100414 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200415 if (updateFunctions.TryGetValue(pathToSubmodelElement, out Action<IValue> updateFunction))
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100416 updateFunction.Invoke(value);
417
418 }
419
420 public IResult<ISubmodel> RetrieveSubmodel()
421 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200422 return new Result<ISubmodel>(_submodel != null, _submodel);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100423 }
424
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200425 public IResult<ISubmodelElement> CreateOrUpdateSubmodelElement(string pathToSubmodelElement, ISubmodelElement submodelElement)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100426 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200427 if (_submodel == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100428 return new Result<ISubmodelElement>(false, new NotFoundMessage("Submodel"));
429
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200430 var created = _submodel.SubmodelElements.CreateOrUpdate(pathToSubmodelElement, submodelElement);
431 if(created.Success && created.Entity != null)
432 RegisterSubmodelElementHandler(pathToSubmodelElement, new SubmodelElementHandler(submodelElement.Get, submodelElement.Set));
433 return created;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100434 }
435
436 public IResult<IElementContainer<ISubmodelElement>> RetrieveSubmodelElements()
437 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200438 if (_submodel == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100439 return new Result<ElementContainer<ISubmodelElement>>(false, new NotFoundMessage("Submodel"));
440
Constantin Ziesche08215502020-09-21 19:08:32 +0200441 if (_submodel.SubmodelElements == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100442 return new Result<ElementContainer<ISubmodelElement>>(false, new NotFoundMessage("SubmodelElements"));
Constantin Ziesche08215502020-09-21 19:08:32 +0200443 return _submodel.SubmodelElements.RetrieveAll();
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100444 }
445
446 public IResult<ISubmodelElement> RetrieveSubmodelElement(string submodelElementId)
447 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200448 if (_submodel == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100449 return new Result<ISubmodelElement>(false, new NotFoundMessage("Submodel"));
450
Constantin Ziesche08215502020-09-21 19:08:32 +0200451 if (_submodel.SubmodelElements == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100452 return new Result<ISubmodelElement>(false, new NotFoundMessage(submodelElementId));
Constantin Ziesche8b4a64d2020-06-25 11:52:09 +0200453
Constantin Ziesche08215502020-09-21 19:08:32 +0200454 return _submodel.SubmodelElements.Retrieve(submodelElementId);
Constantin Ziesche8b4a64d2020-06-25 11:52:09 +0200455 }
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100456 public IResult<IValue> RetrieveSubmodelElementValue(string submodelElementId)
457 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200458 if (_submodel == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100459 return new Result<IValue>(false, new NotFoundMessage("Submodel"));
460
Constantin Ziesche08215502020-09-21 19:08:32 +0200461 if (submodelElementHandler.TryGetValue(submodelElementId, out SubmodelElementHandler elementHandler) && elementHandler.GetValueHandler != null)
462 {
463 var submodelElement = _submodel.SubmodelElements.Retrieve<ISubmodelElement>(submodelElementId);
464 if (submodelElement.Success && submodelElement.Entity != null)
465 return new Result<IValue>(true, elementHandler.GetValueHandler.Invoke(submodelElement.Entity));
466 else
467 return new Result<IValue>(false, new Message(MessageType.Error, "SubmodelElement not found"));
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200468 }
Constantin Ziesche08215502020-09-21 19:08:32 +0200469 else
470 return new Result<IValue>(false, new Message(MessageType.Error, "SubmodelElementHandler not found"));
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100471 }
472
Constantin Ziesche08215502020-09-21 19:08:32 +0200473 public IResult UpdateSubmodelElementValue(string submodelElementId, IValue value)
474 {
475 if (_submodel == null)
476 return new Result(false, new NotFoundMessage("Submodel"));
477
478 if (submodelElementHandler.TryGetValue(submodelElementId, out SubmodelElementHandler elementHandler) && elementHandler.SetValueHandler != null)
479 {
480 var submodelElement = _submodel.SubmodelElements.Retrieve<ISubmodelElement>(submodelElementId);
481 if (submodelElement.Success && submodelElement.Entity != null)
482 {
483 elementHandler.SetValueHandler.Invoke(submodelElement.Entity, value);
484 return new Result(true);
485 }
486 else
487 return new Result<IValue>(false, new Message(MessageType.Error, "property not found"));
488 }
489 else
490 return new Result<IValue>(false, new Message(MessageType.Error, "SubmodelElementHandler not found"));
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100491 }
492
493 public IResult DeleteSubmodelElement(string submodelElementId)
494 {
Constantin Ziesche08215502020-09-21 19:08:32 +0200495 if (_submodel == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100496 return new Result(false, new NotFoundMessage("Submodel"));
497
Constantin Ziesche08215502020-09-21 19:08:32 +0200498 if (_submodel.SubmodelElements == null)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100499 return new Result(false, new NotFoundMessage(submodelElementId));
500
Constantin Ziesche09dcb6b2020-10-07 13:47:39 +0200501 var deleted = _submodel.SubmodelElements.Delete(submodelElementId);
502 if (deleted.Success)
503 UnregisterSubmodelElementHandler(submodelElementId);
504 return deleted;
Constantin Zieschee837f992020-08-19 12:04:32 +0200505 }
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100506 }
507}