init
This commit is contained in:
376
GLM5Analysis/PatternLibrary/ServicePattern.md
Normal file
376
GLM5Analysis/PatternLibrary/ServicePattern.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# Service Pattern (Background Services)
|
||||
|
||||
## When to Use
|
||||
- Implementing hardware communication (SLICE, TDAS, Ribeye)
|
||||
- Long-running background operations
|
||||
- Real-time data acquisition
|
||||
- Download operations from hardware devices
|
||||
- Diagnostics and calibration services
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ ServiceBase │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Callback Pattern: ServiceCallback(CallbackData) │ │
|
||||
│ │ - Progress: Report progress percentage │ │
|
||||
│ │ - NewData: Real-time sample data │ │
|
||||
│ │ - Success/Failure: Operation completion │ │
|
||||
│ │ - Canceled: User cancellation │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ State Machine: Stateless-based state transitions │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
||||
│ │ Prepare │───>│Configure│───>│ Arm │───>│ Realtime │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ v v v v │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
||||
│ │ Diagnose│ │ Download│ │ Disarm │ │ Stop │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
### Structure
|
||||
```
|
||||
IService/
|
||||
├── Classes/
|
||||
│ ├── ServiceBase.cs (Base class for all services)
|
||||
│ ├── GenericServices.cs (Service orchestration)
|
||||
│ ├── SLICEService/
|
||||
│ │ ├── SLICE Service.cs
|
||||
│ │ ├── SLICE Service.Configuration.cs
|
||||
│ │ ├── SLICE Service.Realtime.cs
|
||||
│ │ └── SLICE Service.Download.cs
|
||||
│ └── TDAS Service/
|
||||
├── Interfaces/
|
||||
│ ├── IConfigurationActions.cs
|
||||
│ ├── IRealTimeActions.cs
|
||||
│ ├── IDownloadActions.cs
|
||||
│ └── IDiagnosticsActions.cs
|
||||
└── StateMachine/
|
||||
├── States.cs
|
||||
├── Triggers.cs
|
||||
└── States/
|
||||
├── Configure.cs
|
||||
├── Realtime.cs
|
||||
└── Download.cs
|
||||
```
|
||||
|
||||
## Code Templates
|
||||
|
||||
### 1. Service Base Class Pattern
|
||||
```csharp
|
||||
using System;
|
||||
using System.Threading;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.DASLib.Service
|
||||
{
|
||||
public abstract class ServiceBase : IDisposable
|
||||
{
|
||||
public bool AggregateProgress { get; set; }
|
||||
|
||||
public class CallbackData
|
||||
{
|
||||
public enum CallbackStatus
|
||||
{
|
||||
Progress,
|
||||
NewData,
|
||||
AllFinished,
|
||||
Success,
|
||||
Failure,
|
||||
Canceled
|
||||
}
|
||||
|
||||
public CallbackStatus Status { get; set; }
|
||||
public IDASCommunication Target { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
public Exception ErrorException { get; set; }
|
||||
public int ProgressValue { get; set; }
|
||||
public SampleData[] Data { get; set; }
|
||||
}
|
||||
|
||||
public delegate void Callback(CallbackData data);
|
||||
public delegate void ServiceBaseEventHandler(object sender, CallbackData data);
|
||||
|
||||
public event ServiceBaseEventHandler ServiceAvailable;
|
||||
public event ServiceCallbackErrorEventHandler ServiceCallbackError;
|
||||
|
||||
public abstract string ServiceName();
|
||||
|
||||
public virtual void Cancel()
|
||||
{
|
||||
APILogger.LogString($"Entering {ServiceName()}.Cancel");
|
||||
DASServiceLock.Cancel(this);
|
||||
}
|
||||
|
||||
protected void RunService(Callback userCallback, object userObject)
|
||||
{
|
||||
var glue = new ServiceGlueClass(userCallback, userObject);
|
||||
// Service orchestration logic
|
||||
}
|
||||
|
||||
protected void FireCallback(CallbackData data)
|
||||
{
|
||||
// Fire callback to user
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Hardware Service Implementation
|
||||
```csharp
|
||||
using System;
|
||||
using System.Threading;
|
||||
using DTS.Common.DASResource;
|
||||
using DTS.Common.Interface.DASFactory;
|
||||
|
||||
namespace DTS.DASLib.Service
|
||||
{
|
||||
public class SLICEService : ServiceBase,
|
||||
IConfigurationActions,
|
||||
IRealTimeActions,
|
||||
IDownloadActions
|
||||
{
|
||||
public override string ServiceName() => "SLICE Service";
|
||||
|
||||
#region Configuration
|
||||
|
||||
public void Configure(
|
||||
IDASCommunication[] targets,
|
||||
IServiceConfiguration config,
|
||||
Callback callback,
|
||||
object userObject)
|
||||
{
|
||||
APILogger.LogString($"{ServiceName()}.Configure starting");
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var slice = target as ISLICE;
|
||||
if (slice == null) continue;
|
||||
|
||||
ApplyConfiguration(slice, config);
|
||||
}
|
||||
|
||||
callback?.Invoke(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.Success,
|
||||
Target = null
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
callback?.Invoke(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.Failure,
|
||||
ErrorException = ex,
|
||||
ErrorMessage = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Realtime
|
||||
|
||||
public void StartRealtime(
|
||||
IDASCommunication[] targets,
|
||||
Callback callback,
|
||||
object userObject)
|
||||
{
|
||||
APILogger.LogString($"{ServiceName()}.StartRealtime");
|
||||
|
||||
RunService(callback, userObject);
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var slice = target as ISLICE;
|
||||
slice?.StartRealtime(OnRealtimeData, target);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRealtimeData(SampleData[] data, object context)
|
||||
{
|
||||
FireCallback(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.NewData,
|
||||
Data = data,
|
||||
Target = context as IDASCommunication
|
||||
});
|
||||
}
|
||||
|
||||
public void StopRealtime(IDASCommunication[] targets)
|
||||
{
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var slice = target as ISLICE;
|
||||
slice?.StopRealtime();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Download
|
||||
|
||||
public void Download(
|
||||
IDASCommunication[] targets,
|
||||
IDownloadParameters parameters,
|
||||
Callback callback,
|
||||
object userObject)
|
||||
{
|
||||
APILogger.LogString($"{ServiceName()}.Download");
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var slice = target as ISLICE;
|
||||
slice?.Download(
|
||||
parameters,
|
||||
(progress, data, error) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
callback?.Invoke(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.Failure,
|
||||
ErrorMessage = error,
|
||||
Target = target
|
||||
});
|
||||
}
|
||||
else if (data != null)
|
||||
{
|
||||
callback?.Invoke(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.NewData,
|
||||
Data = data,
|
||||
ProgressValue = progress,
|
||||
Target = target
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
callback?.Invoke(new CallbackData
|
||||
{
|
||||
Status = CallbackData.CallbackStatus.AllFinished,
|
||||
Target = null
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Service Interface
|
||||
```csharp
|
||||
namespace DTS.DASLib.Service.Interfaces
|
||||
{
|
||||
public interface IRealTimeActions
|
||||
{
|
||||
void StartRealtime(IDASCommunication[] targets, ServiceBase.Callback callback, object userObject);
|
||||
void StopRealtime(IDASCommunication[] targets);
|
||||
}
|
||||
|
||||
public interface IDownloadActions
|
||||
{
|
||||
void Download(IDASCommunication[] targets, IDownloadParameters parameters,
|
||||
ServiceBase.Callback callback, object userObject);
|
||||
}
|
||||
|
||||
public interface IConfigurationActions
|
||||
{
|
||||
void Configure(IDASCommunication[] targets, IServiceConfiguration config,
|
||||
ServiceBase.Callback callback, object userObject);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples from Codebase
|
||||
|
||||
### Example 1: ServiceBase Callback Pattern
|
||||
**File:** `DataPRO/IService/Classes/GenericServices.cs:35-122`
|
||||
```csharp
|
||||
public abstract class ServiceBase : IDisposable
|
||||
{
|
||||
public class CallbackData
|
||||
{
|
||||
public enum CallbackStatus { Progress, NewData, AllFinished, Success, Failure, Canceled }
|
||||
|
||||
public CallbackStatus Status { get; set; }
|
||||
public IDASCommunication Target { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
public Exception ErrorException { get; set; }
|
||||
public int ProgressValue { get; set; }
|
||||
public SampleData[] Data { get; set; }
|
||||
}
|
||||
|
||||
public delegate void Callback(CallbackData data);
|
||||
public event ServiceBaseEventHandler ServiceAvailable;
|
||||
|
||||
public abstract string ServiceName();
|
||||
|
||||
public virtual void Cancel()
|
||||
{
|
||||
APILogger.LogString($"Entering {ServiceName()}.Cancel");
|
||||
DASServiceLock.Cancel(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Service with Progress Reporting
|
||||
**File:** `DataPRO/IService/Classes/GenericServices.cs:198`
|
||||
```csharp
|
||||
protected void ServiceIsAvailable(object sender, CallbackData data)
|
||||
{
|
||||
ServiceAvailable -= ServiceIsAvailable;
|
||||
// Handle service completion
|
||||
}
|
||||
|
||||
protected class ServiceGlueClass
|
||||
{
|
||||
public object UserObject { get; set; }
|
||||
public Callback UserCallback { get; set; }
|
||||
public ManualResetEvent AvailableEvent { get; set; }
|
||||
|
||||
public ServiceGlueClass(Callback userCallback, object userObject)
|
||||
{
|
||||
UserObject = userObject;
|
||||
UserCallback = userCallback;
|
||||
AvailableEvent = new ManualResetEvent(false);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example 3: DASModule Configuration
|
||||
**File:** `DataPRO/IService/Classes/DASModule.cs`
|
||||
```csharp
|
||||
public class DASModule
|
||||
{
|
||||
public List<AnalogInputDASChannel> AnalogInputChannels { get; set; }
|
||||
public List<DigitalOutputDASChannel> DigitalOutputChannels { get; set; }
|
||||
|
||||
public void Configure(IServiceConfiguration config)
|
||||
{
|
||||
// Apply configuration to all channels
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
1. **Not handling cancellation** - Always implement `Cancel()` to clean up resources
|
||||
2. **Blocking the callback thread** - Use async patterns, don't block in callbacks
|
||||
3. **Missing error handling in callbacks** - Always check for null callback before invoking
|
||||
4. **Not disposing connections** - Implement `IDisposable` properly
|
||||
5. **Fire-and-forget without error logging** - Always log errors even if no callback
|
||||
6. **Cross-thread UI updates** - Use `Dispatcher.Invoke` for UI updates from service callbacks
|
||||
7. **Not using ServiceAvailable event** - Must unsubscribe to prevent memory leaks
|
||||
8. **Ignoring Target in CallbackData** - Needed to identify which unit responded
|
||||
9. **Not checking AggregateProgress** - Affects how progress is reported for multiple units
|
||||
10. **Missing ServiceName() override** - Required for logging identification
|
||||
Reference in New Issue
Block a user