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 -
Mass Flow (SCCM)
+
Mass Flow
@($"{ViewModel!.AlicatState.LiveData.MassFlow}")
@@ -48,7 +48,7 @@
} -
Setpoint (SCCM)
+
Setpoint
@($"{ViewModel!.AlicatState.LiveData.Setpoint}")
New Setpoint