Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 1 | /******************************************************************************* |
| 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 | *******************************************************************************/ |
| 11 | using BaSyx.API.AssetAdministrationShell; |
| 12 | using BaSyx.Models.Core.AssetAdministrationShell.Generics; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 13 | using BaSyx.Utils.ResultHandling; |
| 14 | using BaSyx.Utils.Client; |
| 15 | using System.Collections.Generic; |
| 16 | using System; |
| 17 | using Newtonsoft.Json; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 18 | using System.Linq; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 19 | using BaSyx.Models.Connectivity.Descriptors; |
| 20 | using BaSyx.Models.Core.Common; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 21 | using BaSyx.Models.Communication; |
| 22 | using System.Threading.Tasks; |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 23 | using NLog; |
| 24 | using System.Threading; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 25 | |
| 26 | namespace BaSyx.API.Components |
| 27 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 28 | /// <summary> |
| 29 | /// Reference implementation of ISubmodelServiceProvider interface |
| 30 | /// </summary> |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 31 | public class SubmodelServiceProvider : ISubmodelServiceProvider |
| 32 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 33 | private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); |
| 34 | |
| 35 | private ISubmodel _submodel; |
| 36 | |
Constantin Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 37 | public const int DEFAULT_TIMEOUT = 30000; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 38 | public ISubmodelDescriptor ServiceDescriptor { get; internal set; } |
| 39 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 40 | private readonly Dictionary<string, MethodCalledHandler> methodCalledHandler; |
| 41 | private readonly Dictionary<string, SubmodelElementHandler> submodelElementHandler; |
Constantin Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 42 | private readonly Dictionary<string, Action<IValue>> updateFunctions; |
| 43 | private readonly Dictionary<string, EventDelegate> eventDelegates; |
| 44 | private readonly Dictionary<string, InvocationResponse> invocationResults; |
Constantin Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 45 | |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 46 | private IMessageClient messageClient; |
| 47 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 48 | /// <summary> |
| 49 | /// Constructor for SubmodelServiceProvider |
| 50 | /// </summary> |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 51 | public SubmodelServiceProvider() |
| 52 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 53 | methodCalledHandler = new Dictionary<string, MethodCalledHandler>(); |
| 54 | submodelElementHandler = new Dictionary<string, SubmodelElementHandler>(); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 55 | updateFunctions = new Dictionary<string, Action<IValue>>(); |
| 56 | eventDelegates = new Dictionary<string, EventDelegate>(); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 57 | invocationResults = new Dictionary<string, InvocationResponse>(); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 58 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 59 | /// <summary> |
| 60 | /// Contructor for SubmodelServiceProvider with a Submodel object to bind to |
| 61 | /// </summary> |
| 62 | /// <param name="submodel">Submodel object</param> |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 63 | public SubmodelServiceProvider(ISubmodel submodel) : this() |
| 64 | { |
| 65 | BindTo(submodel); |
| 66 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 67 | /// <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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 72 | public SubmodelServiceProvider(ISubmodel submodel, ISubmodelDescriptor submodelDescriptor) : this() |
| 73 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 74 | _submodel = submodel; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 75 | ServiceDescriptor = submodelDescriptor; |
| 76 | } |
| 77 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 78 | /// <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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 83 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 84 | _submodel = submodel; |
| 85 | ServiceDescriptor = new SubmodelDescriptor(submodel, null); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 86 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 87 | |
| 88 | /// <summary> |
| 89 | /// Returns the model binding of this SubmodelServiceProvider |
| 90 | /// </summary> |
| 91 | /// <returns>Submodel object</returns> |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 92 | public ISubmodel GetBinding() |
| 93 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 94 | return _submodel; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 95 | } |
| 96 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 97 | public void UseInMemorySubmodelElementHandler() |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 98 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 99 | 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 106 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 107 | UseInMemorySubmodelElementHandlerInternal(child); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 108 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 109 | } |
| 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 115 | } |
| 116 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 117 | /// <summary> |
| 118 | /// Use as specific SubmodelElementHandler for all SubmodelElements |
| 119 | /// </summary> |
| 120 | /// <param name="elementHandler">SubmodelElementHandler</param> |
| 121 | public void UseSubmodelElementHandler(SubmodelElementHandler elementHandler) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 122 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 123 | 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 133 | } |
| 134 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 135 | 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 158 | public void UseOperationHandler(MethodCalledHandler methodCalledHandler) |
| 159 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 160 | UseOperationHandlerInternal(_submodel.SubmodelElements, methodCalledHandler); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 161 | } |
| 162 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 163 | private void UseOperationHandlerInternal(IElementContainer<ISubmodelElement> submodelElements, MethodCalledHandler methodCalledHandler) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 164 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 165 | if (submodelElements.HasChildren()) |
| 166 | { |
| 167 | foreach (var child in submodelElements.Children) |
| 168 | { |
| 169 | UseOperationHandlerInternal(child, methodCalledHandler); |
| 170 | } |
| 171 | } |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 172 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 173 | { |
| 174 | if (submodelElements.Value is IOperation) |
| 175 | RegisterMethodCalledHandler(submodelElements.Path, methodCalledHandler); |
| 176 | else |
| 177 | return; |
| 178 | } |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 179 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 180 | |
| 181 | public MethodCalledHandler RetrieveMethodCalledHandler(string pathToOperation) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 182 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 183 | if (methodCalledHandler.TryGetValue(pathToOperation, out MethodCalledHandler handler)) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 184 | return handler; |
| 185 | else |
| 186 | return null; |
| 187 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 188 | |
| 189 | public SubmodelElementHandler RetrieveSubmodelElementHandler(string pathToElement) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 190 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 191 | if (submodelElementHandler.TryGetValue(pathToElement, out SubmodelElementHandler elementHandler)) |
| 192 | return elementHandler; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 193 | else |
| 194 | return null; |
| 195 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 196 | |
| 197 | public void RegisterSubmodelElementHandler(string pathToElement, SubmodelElementHandler elementHandler) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 198 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 199 | if (!submodelElementHandler.ContainsKey(pathToElement)) |
| 200 | submodelElementHandler.Add(pathToElement, elementHandler); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 201 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 202 | submodelElementHandler[pathToElement] = elementHandler; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 203 | } |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 204 | |
| 205 | public void UnregisterSubmodelElementHandler(string pathToElement) |
| 206 | { |
| 207 | if (submodelElementHandler.ContainsKey(pathToElement)) |
| 208 | submodelElementHandler.Remove(pathToElement); |
| 209 | } |
| 210 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 211 | public void RegisterMethodCalledHandler(string pathToOperation, MethodCalledHandler handler) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 212 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 213 | if (!methodCalledHandler.ContainsKey(pathToOperation)) |
| 214 | methodCalledHandler.Add(pathToOperation, handler); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 215 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 216 | methodCalledHandler[pathToOperation] = handler; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 217 | } |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 218 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 219 | public void RegisterEventDelegate(string pathToEvent, EventDelegate eventDelegate) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 220 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 221 | if (!eventDelegates.ContainsKey(pathToEvent)) |
| 222 | eventDelegates.Add(pathToEvent, eventDelegate); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 223 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 224 | eventDelegates[pathToEvent] = eventDelegate; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 225 | } |
| 226 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 227 | public IResult<InvocationResponse> InvokeOperation(string pathToOperation, InvocationRequest invocationRequest) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 228 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 229 | 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 Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 246 | int timeout = DEFAULT_TIMEOUT; |
| 247 | if (invocationRequest.Timeout.HasValue) |
| 248 | timeout = invocationRequest.Timeout.Value; |
| 249 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 250 | 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 Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 269 | if (Task.WhenAny(runner, Task.Delay(timeout, cancellationTokenSource.Token)).Result == runner) |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 270 | { |
| 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 285 | } |
| 286 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 287 | public IResult<CallbackResponse> InvokeOperationAsync(string pathToOperation, InvocationRequest invocationRequest) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 288 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 289 | if (_submodel == null) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 290 | return new Result<CallbackResponse>(false, new NotFoundMessage("Submodel")); |
| 291 | if (invocationRequest == null) |
| 292 | return new Result<CallbackResponse>(new ArgumentNullException(nameof(invocationRequest))); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 293 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 294 | var operation_Retrieved = _submodel.SubmodelElements.Retrieve<IOperation>(pathToOperation); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 295 | if (operation_Retrieved.Success && operation_Retrieved.Entity != null) |
| 296 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 297 | MethodCalledHandler methodHandler; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 298 | if (operation_Retrieved.Entity.OnMethodCalled != null) |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 299 | methodHandler = operation_Retrieved.Entity.OnMethodCalled; |
| 300 | else if (methodCalledHandler.TryGetValue(pathToOperation, out MethodCalledHandler handler)) |
| 301 | methodHandler = handler; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 302 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 303 | return new Result<CallbackResponse>(false, new NotFoundMessage($"MethodHandler for {pathToOperation}")); |
Constantin Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 304 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 305 | Task invocationTask = Task.Run(async() => |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 306 | { |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 307 | InvocationResponse invocationResponse = new InvocationResponse(invocationRequest.RequestId); |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 308 | invocationResponse.InOutputArguments = invocationRequest.InOutputArguments; |
| 309 | SetInvocationResult(pathToOperation, invocationRequest.RequestId, ref invocationResponse); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 310 | |
Constantin Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 311 | int timeout = DEFAULT_TIMEOUT; |
| 312 | if (invocationRequest.Timeout.HasValue) |
| 313 | timeout = invocationRequest.Timeout.Value; |
| 314 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 315 | using (var cancellationTokenSource = new CancellationTokenSource()) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 316 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 317 | 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 Ziesche | b28a71c | 2020-10-13 08:39:42 +0200 | [diff] [blame] | 333 | if (await Task.WhenAny(runner, Task.Delay(timeout, cancellationTokenSource.Token)) == runner) |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 334 | { |
| 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 Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 344 | } |
| 345 | }); |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 346 | |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 347 | string endpoint = ServiceDescriptor?.Endpoints?.FirstOrDefault()?.Address; |
| 348 | CallbackResponse callbackResponse = new CallbackResponse(invocationRequest.RequestId); |
| 349 | if (string.IsNullOrEmpty(endpoint)) |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 350 | callbackResponse.CallbackUrl = new Uri($"/submodelElements/{pathToOperation}/invocationList/{invocationRequest.RequestId}", UriKind.Relative); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 351 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 352 | callbackResponse.CallbackUrl = new Uri($"{endpoint}/submodelElements/{pathToOperation}/invocationList/{invocationRequest.RequestId}", UriKind.Absolute); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 353 | 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 Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 370 | |
| 371 | public IResult<InvocationResponse> GetInvocationResult(string pathToOperation, string requestId) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 372 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 373 | string key = string.Join("_", pathToOperation, requestId); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 374 | 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 Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 382 | } |
| 383 | |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 384 | 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 Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 390 | return new Result(new ArgumentNullException(nameof(publishableEvent))); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 391 | |
| 392 | if (eventDelegates.TryGetValue(publishableEvent.Name, out EventDelegate eventDelegate)) |
| 393 | eventDelegate.Invoke(this, publishableEvent); |
| 394 | |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 395 | string message = JsonConvert.SerializeObject(publishableEvent, Formatting.Indented); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 396 | return messageClient.Publish(topic, message, MessagePublished, qosLevel, retain); |
| 397 | } |
| 398 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 399 | |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 400 | public virtual void ConfigureEventHandler(IMessageClient messageClient) |
| 401 | { |
| 402 | this.messageClient = messageClient; |
| 403 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 404 | |
| 405 | public virtual void SubscribeUpdates(string pathToSubmodelElement, Action<IValue> updateFunction) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 406 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 407 | if (!updateFunctions.ContainsKey(pathToSubmodelElement)) |
| 408 | updateFunctions.Add(pathToSubmodelElement, updateFunction); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 409 | else |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 410 | updateFunctions[pathToSubmodelElement] = updateFunction; |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 411 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 412 | |
| 413 | public virtual void PublishUpdate(string pathToSubmodelElement, IValue value) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 414 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 415 | if (updateFunctions.TryGetValue(pathToSubmodelElement, out Action<IValue> updateFunction)) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 416 | updateFunction.Invoke(value); |
| 417 | |
| 418 | } |
| 419 | |
| 420 | public IResult<ISubmodel> RetrieveSubmodel() |
| 421 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 422 | return new Result<ISubmodel>(_submodel != null, _submodel); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 423 | } |
| 424 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 425 | public IResult<ISubmodelElement> CreateOrUpdateSubmodelElement(string pathToSubmodelElement, ISubmodelElement submodelElement) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 426 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 427 | if (_submodel == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 428 | return new Result<ISubmodelElement>(false, new NotFoundMessage("Submodel")); |
| 429 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 430 | 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 434 | } |
| 435 | |
| 436 | public IResult<IElementContainer<ISubmodelElement>> RetrieveSubmodelElements() |
| 437 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 438 | if (_submodel == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 439 | return new Result<ElementContainer<ISubmodelElement>>(false, new NotFoundMessage("Submodel")); |
| 440 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 441 | if (_submodel.SubmodelElements == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 442 | return new Result<ElementContainer<ISubmodelElement>>(false, new NotFoundMessage("SubmodelElements")); |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 443 | return _submodel.SubmodelElements.RetrieveAll(); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 444 | } |
| 445 | |
| 446 | public IResult<ISubmodelElement> RetrieveSubmodelElement(string submodelElementId) |
| 447 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 448 | if (_submodel == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 449 | return new Result<ISubmodelElement>(false, new NotFoundMessage("Submodel")); |
| 450 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 451 | if (_submodel.SubmodelElements == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 452 | return new Result<ISubmodelElement>(false, new NotFoundMessage(submodelElementId)); |
Constantin Ziesche | 8b4a64d | 2020-06-25 11:52:09 +0200 | [diff] [blame] | 453 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 454 | return _submodel.SubmodelElements.Retrieve(submodelElementId); |
Constantin Ziesche | 8b4a64d | 2020-06-25 11:52:09 +0200 | [diff] [blame] | 455 | } |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 456 | public IResult<IValue> RetrieveSubmodelElementValue(string submodelElementId) |
| 457 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 458 | if (_submodel == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 459 | return new Result<IValue>(false, new NotFoundMessage("Submodel")); |
| 460 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 461 | 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 Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 468 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 469 | else |
| 470 | return new Result<IValue>(false, new Message(MessageType.Error, "SubmodelElementHandler not found")); |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 471 | } |
| 472 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 473 | 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 Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 491 | } |
| 492 | |
| 493 | public IResult DeleteSubmodelElement(string submodelElementId) |
| 494 | { |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 495 | if (_submodel == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 496 | return new Result(false, new NotFoundMessage("Submodel")); |
| 497 | |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 498 | if (_submodel.SubmodelElements == null) |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 499 | return new Result(false, new NotFoundMessage(submodelElementId)); |
| 500 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 501 | var deleted = _submodel.SubmodelElements.Delete(submodelElementId); |
| 502 | if (deleted.Success) |
| 503 | UnregisterSubmodelElementHandler(submodelElementId); |
| 504 | return deleted; |
Constantin Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 505 | } |
Constantin Ziesche | 857c7ab | 2020-02-25 11:24:51 +0100 | [diff] [blame] | 506 | } |
| 507 | } |