Skip to content

Commit ed5b8f4

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents c645500 + 6504503 commit ed5b8f4

File tree

5 files changed

+236
-6
lines changed

5 files changed

+236
-6
lines changed

src/Moryx.Runtime.Endpoints/Databases/Endpoint/DatabaseController.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2023, Phoenix Contact GmbH & Co. KG
1+
// Copyright (c) 2025, Phoenix Contact GmbH & Co. KG
22
// Licensed under the Apache License, Version 2.0
33

44
using System;
@@ -30,7 +30,13 @@ public class DatabaseController : ControllerBase
3030
{
3131
private readonly IDbContextManager _dbContextManager;
3232
private readonly IDatabaseConfigUpdateService _databaseUpdateService;
33-
private readonly string _dataDirectory = @".\Backups\";
33+
private static readonly string DataDirectory;
34+
35+
static DatabaseController()
36+
{
37+
var executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
38+
DataDirectory = Path.Combine(executingDirectory!, "Backups");
39+
}
3440

3541
public DatabaseController(IDbContextManager dbContextManager)
3642
{
@@ -183,7 +189,7 @@ public ActionResult<InvocationResponse> DumpDatabase(string targetModel, Databas
183189
return BadConfigValues();
184190

185191

186-
var targetPath = Path.Combine(_dataDirectory, targetModel);
192+
var targetPath = Path.Combine(DataDirectory, targetModel);
187193
if (!Directory.Exists(targetPath))
188194
Directory.CreateDirectory(targetPath);
189195

@@ -204,7 +210,7 @@ public ActionResult<InvocationResponse> RestoreDatabase(string targetModel, Rest
204210
if (!IsConfigValid(updatedConfig))
205211
return BadConfigValues();
206212

207-
var filePath = Path.Combine(_dataDirectory, targetModel, request.BackupFileName);
213+
var filePath = Path.Combine(DataDirectory, targetModel, request.BackupFileName);
208214
targetConfigurator.RestoreDatabase(updatedConfig, filePath);
209215

210216
return new InvocationResponse();
@@ -355,7 +361,7 @@ private IEnumerable<SetupModel> GetAllSetups(Type contextType)
355361
var setups = allSetups.Where(setup => string.IsNullOrEmpty(setup.SupportedFileRegex))
356362
.Select(ConvertSetup).OrderBy(setup => setup.SortOrder).ToList();
357363
string[] files;
358-
if (!Directory.Exists(_dataDirectory) || !(files = Directory.GetFiles(_dataDirectory)).Any())
364+
if (!Directory.Exists(DataDirectory) || !(files = Directory.GetFiles(DataDirectory)).Any())
359365
return setups.ToArray();
360366

361367
var fileSetups = allSetups.Where(setup => !string.IsNullOrEmpty(setup.SupportedFileRegex))
@@ -372,7 +378,7 @@ private IEnumerable<SetupModel> GetAllSetups(Type contextType)
372378
private IEnumerable<BackupModel> GetAllBackups(Type contextType)
373379
{
374380
var targetModel = TargetModelName(contextType);
375-
var backupFolder = Path.Combine(_dataDirectory, targetModel);
381+
var backupFolder = Path.Combine(DataDirectory, targetModel);
376382

377383
if (!Directory.Exists(backupFolder))
378384
return Array.Empty<BackupModel>();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using Moryx.Container;
2+
using Moryx.Tools.FunctionResult;
3+
using System;
4+
5+
namespace Moryx.Communication.Connection
6+
{
7+
/// <summary>
8+
/// Specialized interface of <see cref="IDeviceCommunication"/>
9+
/// </summary>
10+
public interface IConnectionAttempt : IDeviceCommunication;
11+
12+
/// <summary>
13+
/// Implements a common process of establishing connections of any kind
14+
/// which is defined by the user.
15+
/// </summary>
16+
[Plugin(LifeCycle.Transient, [typeof(IConnectionAttempt)])]
17+
public class ConnectionAttempt : DeviceCommunicationBase, IConnectionAttempt
18+
{
19+
/// <summary>
20+
/// Retry in case a connection couldn't be established
21+
/// </summary>
22+
public bool RetryOnFailure { get; set; } = true;
23+
24+
/// <summary>
25+
/// Reconnect timeout in milliseconds
26+
/// </summary>
27+
public int TimeoutInMs { get; set; } = 10000;
28+
29+
/// <summary>
30+
/// Starts trying to connect a connection and checks its status.
31+
/// </summary>
32+
public override void Start(Func<FunctionResult> communicate)
33+
{
34+
if (_started)
35+
return;
36+
37+
_execute = communicate
38+
?? throw new ArgumentException(nameof(communicate));
39+
_started = true;
40+
41+
Start();
42+
}
43+
44+
private void Start()
45+
{
46+
var result = _execute!.Invoke();
47+
if (result.Success)
48+
{
49+
RaiseExecutedEvent();
50+
}
51+
else
52+
{
53+
RaiseFailureEvent(result);
54+
if (RetryOnFailure)
55+
_timerId = ParallelOperations.ScheduleExecution(
56+
Start,
57+
TimeoutInMs,
58+
0);
59+
}
60+
}
61+
}
62+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Moryx.Container;
2+
using Moryx.Threading;
3+
using Moryx.Tools.FunctionResult;
4+
using System;
5+
6+
namespace Moryx.Communication.Connection
7+
{
8+
9+
/// <summary>
10+
/// Implements a common process of establishing connections of any kind
11+
/// which is defined by the user.
12+
/// It handles the need of executing frequent status checks when a connection
13+
/// could be established or further connection attempts when not.
14+
/// </summary>
15+
public abstract class DeviceCommunicationBase : IDeviceCommunication
16+
{
17+
/// <inheritdoc/>
18+
protected int _timerId;
19+
20+
/// <inheritdoc/>
21+
protected bool _started = false;
22+
23+
/// <inheritdoc/>
24+
protected Func<FunctionResult> _execute;
25+
26+
/// <inheritdoc />
27+
public event EventHandler Executed;
28+
/// <inheritdoc />
29+
public event EventHandler<FunctionResult> Failed;
30+
31+
/// <summary>
32+
/// <see cref="IParallelOperations"/> to enalbe derived types
33+
/// to execute parallel operations
34+
/// </summary>
35+
public IParallelOperations ParallelOperations { get; set; }
36+
37+
/// <inheritdoc />
38+
public abstract void Start(Func<FunctionResult> execute);
39+
40+
/// <inheritdoc />
41+
public virtual void Stop()
42+
{
43+
ParallelOperations.StopExecution(_timerId);
44+
_started = false;
45+
}
46+
47+
/// <summary>
48+
/// Raises the <see cref="Executed"/> event
49+
/// </summary>
50+
protected void RaiseExecutedEvent()
51+
{
52+
Executed?.Invoke(this, EventArgs.Empty);
53+
}
54+
55+
/// <summary>
56+
/// Raises the <see cref="Failed"/> event
57+
/// </summary>
58+
/// <param name="result">A <see cref="FunctionResult"/> to be evaluated by the caller</param>
59+
protected void RaiseFailureEvent(FunctionResult result)
60+
{
61+
Failed?.Invoke(this, result);
62+
}
63+
}
64+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Moryx.Tools.FunctionResult;
2+
using System;
3+
4+
namespace Moryx.Communication.Connection
5+
{
6+
/// <summary>
7+
/// General interface to model any kind of communication
8+
/// to and with devices
9+
/// </summary>
10+
public interface IDeviceCommunication
11+
{
12+
/// <summary>
13+
/// Starts communication
14+
/// </summary>
15+
/// <param name="communicate">The procedure that executes
16+
/// this communication</param>
17+
void Start(Func<FunctionResult> communicate);
18+
19+
/// <summary>
20+
/// Stops the communication
21+
/// </summary>
22+
void Stop();
23+
24+
/// <summary>
25+
/// Event handler to be invoked when the communication
26+
/// could be executed.
27+
/// </summary>
28+
event EventHandler Executed;
29+
30+
/// <summary>
31+
/// Event handler to be invoked when there was any error
32+
/// during communication
33+
/// </summary>
34+
event EventHandler<FunctionResult> Failed;
35+
}
36+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using Moryx.Container;
2+
using Moryx.Tools.FunctionResult;
3+
using System;
4+
5+
namespace Moryx.Communication.Connection
6+
{
7+
/// <summary>
8+
/// Specialized interface of <see cref="IDeviceCommunication"/>
9+
/// </summary>
10+
public interface IStatusCheck : IDeviceCommunication;
11+
12+
/// <summary>
13+
/// Implements a common process of executing status checks.
14+
/// </summary>
15+
[Plugin(LifeCycle.Transient, [typeof(IStatusCheck)])]
16+
public class StatusCheck : DeviceCommunicationBase, IStatusCheck
17+
{
18+
/// <summary>
19+
/// Interval in which the status check is executed
20+
/// </summary>
21+
public int IntervalInMs { get; set; } = 5000;
22+
23+
/// <summary>
24+
/// Starts intervally checking the status
25+
/// </summary>
26+
public override void Start(Func<FunctionResult> execute)
27+
{
28+
if (_started)
29+
return;
30+
31+
_execute = execute;
32+
_started = true;
33+
34+
StartStatusCheck();
35+
}
36+
37+
private void StartStatusCheck()
38+
{
39+
_timerId = ParallelOperations.ScheduleExecution(
40+
CheckStatus,
41+
IntervalInMs,
42+
0);
43+
}
44+
45+
private void CheckStatus()
46+
{
47+
if (_execute != null)
48+
{
49+
var result = _execute.Invoke();
50+
if (result.Success)
51+
{
52+
RaiseExecutedEvent();
53+
StartStatusCheck();
54+
}
55+
else
56+
{
57+
RaiseFailureEvent(result);
58+
}
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)