diff --git a/AlicatBusMFC/AlicatBusMFC.csproj b/AlicatBusMFC/AlicatBusMFC.csproj
new file mode 100644
index 0000000..a3a34b6
--- /dev/null
+++ b/AlicatBusMFC/AlicatBusMFC.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/AlicatMFCRemastered/Connection/MassFlowControllerConnection.cs b/AlicatMFCRemastered/Connection/MassFlowControllerConnection.cs
index 55d31d0..1f28660 100644
--- a/AlicatMFCRemastered/Connection/MassFlowControllerConnection.cs
+++ b/AlicatMFCRemastered/Connection/MassFlowControllerConnection.cs
@@ -6,7 +6,18 @@ namespace AlicatMFCRemastered;
public class MassFlowControllerConnection : AresHardwareConnection, IMfcConnection
{
- private readonly List _unusedIds;
+ //simple registry to manage instances of seriaport resources across multiple mfcs
+ static Dictionary _connections = new Dictionary();
+ public static MassFlowControllerConnection GetMassFlowControllerConnection(string portName)
+ {
+ if (!_connections.ContainsKey(portName))
+ _connections[portName] = new MassFlowControllerConnection(portName);
+
+ return _connections[portName];
+ }
+
+
+ private readonly List _unusedIds;
public MassFlowControllerConnection(string portName) : base(new SerialPortConnectionInfo(19200, Parity.None, 8, StopBits.One), portName,
new SerialConnectionOptions
diff --git a/AlicatMFCRemastered/MassFlowController.cs b/AlicatMFCRemastered/MassFlowController.cs
index 74b3a6e..8d0131d 100644
--- a/AlicatMFCRemastered/MassFlowController.cs
+++ b/AlicatMFCRemastered/MassFlowController.cs
@@ -25,7 +25,7 @@ namespace AlicatMFCRemastered;
public class MassFlowController : AresDevice, IMassFlowController
{
- private readonly int _expectedDataFormatEntryCount;
+ private readonly int _expectedDataFormatEntryCount;
private readonly BehaviorSubject _stateSubject = new(new AresStruct());
private CancellationTokenSource _stateGetterLoopTokenSource = new();
private CompositeDisposable _stateWatchers = new();
@@ -57,12 +57,13 @@ public MassFlowController(DeviceConnectionInfo connectionInfo, ILogger logger) :
}
else
- _serialConnection = new MassFlowControllerConnection(serialInfo.PortName);
+ _serialConnection = MassFlowControllerConnection.GetMassFlowControllerConnection(serialInfo.PortName);
- _stateWatchers = new CompositeDisposable
- {
- _serialConnection.GetTransactionStream().Select(transaction => transaction.Response).Subscribe(UpdateLiveData)
- };
+ /// replaced with transactional update. Could restore if we add an id check to verify correct MFC, but given potential 26 MFCs on a single connection, most traffic on bus could be ignored
+ //_stateWatchers = new CompositeDisposable
+ //{
+ // _serialConnection.GetTransactionStream().Select(transaction => transaction.Response).Subscribe(UpdateLiveData)
+ //};
_expectedDataFormatEntryCount = _mfcType == MfcTypeEnum.Normal ? 12 : 7;
@@ -170,9 +171,12 @@ private void UpdatePotentialMaxValue(ManufacturerInfoEntry entry)
_logger.LogWarning($"Failed to get max value for MFC {Name} as we couldn't get the numeric max value from model number {entry.Data}");
return;
}
- var flowVal = StandardVolumeFlow.From(numericNum, unit);
- dataFrameFormat.MaxVal = flowVal.StandardLitersPerMinute.ToString();
- }
+ _logger.LogInformation($"Found a potential max value of {numericNum} {unit} for MFC {Name} from model number {entry.Data}");
+ var flowVal = StandardVolumeFlow.From(numericNum, unit);
+// must be converted to match setpoint units, otherwise may cause issues when calculating newsetpoint
+ // dataFrameFormat.MaxVal = flowVal.StandardLitersPerMinute.ToString();
+ dataFrameFormat.MaxVal = flowVal.As((StandardVolumeFlowUnit)dataFrameFormat.Unit).ToString();
+ }
}
}
}
@@ -461,9 +465,9 @@ public Task NewComposerMix(MfcGasComposition composerMix)
public async Task NewSetpoint(StandardVolumeFlow setpoint)
{
if(_mfcType == MfcTypeEnum.Normal)
- {
+ {
var newSetpointCommand = new NewSetpointCommand(AssumedId, setpoint, GetFormatEntries(), FirmwareVersion);
- try
+ try
{
var response = await Send(newSetpointCommand, TimeSpan.FromSeconds(10));
}
@@ -655,7 +659,9 @@ public async Task StartUpdateLoop(TimeSpan interval)
try
{
var liveData = await GetLiveData();
- }
+ /// explicit call required here if statewatchers is not used to subscribe to the live data stream, otherwise the state will not update
+ UpdateLiveData(liveData);
+ }
catch(TimeoutException)
{
Status = new DeviceOperationalStatus { OperationalState = OperationalState.Active, Message = $"Get Live Data timed out at {DateTime.Now}" };
@@ -728,6 +734,9 @@ private async Task GetResponseWithRetry(TRequest req
private void UpdateLiveData(LiveDataResponse liveResponse)
{
_liveData = liveResponse;
+ /// check on id is required if _statewatcher is used in order to avoid updating the state with a response from a different MFC than the one that is being watched
+ /// if (liveResponse.Id != this.AssumedId) return;
+
var next = AresStateBuilder
.From(_stateSubject.Value)
.AddStruct("LiveData", b =>
@@ -963,7 +972,8 @@ public override async Task ExecuteCommand(string command, List
-