我和我的同事在做一个项目,其中一些物理设备连接到 Modbus 接口(例如 Lamp),以及一个桌面应用程序,我们使用 nmodbus 包向 modbus 发送请求,并且不时(例如每 1第二)从 Modbus 读取数据。我们有一个经典的读/写冲突。读取没有问题,但有时当我们向 modbus 写入新值时,物理灯会发疯并每 1 秒改变一次状态。
从 modbus 读取数据是不同的任务,因此向 modbus 写入新值也是如此。
我们尝试过的:
- 锁定临界区(仅写入),并在新值到来时忽略读取数据。之后,我们遇到了队列问题并且工作非常缓慢
- CancellationToken - 没有效果,或者我写的不好
目前我们有一个类收集上次状态变化的属性日期(在 IoC 中注册),当新的写入值到来时,我们更新这个属性,并阻止从 Modbus 读取数据。不幸的是,这段代码有时有效,有时无效。
请帮忙。我们对这个问题很着迷,不知道如何解决。
编辑:我发布当前代码。
这是我们执行 getcurrentState 处理程序的任务
public void Start()
{
var cancellationToken = _cancellationTokenSource.Token;
Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(ReadStateDelayInmiliseconds).ConfigureAwait(false);
if ((DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds < _userCommandThresholdInmiliseconds)
continue;
try
{
await _service.getcurrentState().ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Error($"Error while checking current state. Message '{ex.Message}',Stack trace: '{ex.StackTrace}'.");
}
}
},cancellationToken);
}
这是 getcurrentState 处理程序,我们从 Modbus 读取数据
protected override IgnisLightState Handle(GetState request)
{
if ((DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds <= _configuration.UserCommandThresholdInmiliseconds)
{
Logger.Debug($"Ignore before read state from lights,time after last execution: '{(DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds}'.");
return _state;
}
var currentLightState = _state.DeepClone();
foreach (var head in currentLightState.Heads)
{
try
{
ushort startAddress = 0x0000;
ushort numberOfPointsToRead = 0x0006;
var values = _modbusMaster.ReadHoldingRegisters((byte)head.UniqueId,startAddress,numberOfPointsToRead);
var isOn = values[IgnisRegistry.On.Value];
var isEndo = values[IgnisRegistry.Endo.Value];
var isCentrum = values[IgnisRegistry.Centrum.Value];
var tempValue = values[IgnisRegistry.Temp.Value];
var illuminanceValue = values[IgnisRegistry.Vol.Value];
head.ColorValue = Convert.ToInt32(tempValue);
head.IlluminanceValue = Convert.ToInt32(illuminanceValue);
head.IsCentrumOn = Convert.ToBoolean(isCentrum);
head.IsEndoOn = Convert.ToBoolean(isEndo);
head.IsTurnedOn = Convert.ToBoolean(isOn);
if (currentLightState.CameraState != null &&
_configuration.CameraHeadId.HasValue &&
_configuration.CameraHeadId.Value == head.UniqueId)
{
var camMode = values[IgnisRegistry.Cam.Value];
currentLightState.CameraState.IsTurnedOn = Convert.ToBoolean(isOn);
currentLightState.CameraState.CurrentMode = (IgnisCameraMode)Convert.ToInt32(camMode);
}
}
catch (Exception ex)
{
Logger.ErrorFixed(ex,$"Error while getting data from headId {head.UniqueId}.");
}
}
if (_state.Equals(currentLightState)
|| (DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds < _configuration.UserCommandThresholdInmiliseconds)
{
Logger.Debug($"Ignore after read state from lights,time after last execution: '{(DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds}'.");
return _state;
}
foreach (var currentHeadState in currentLightState.Heads)
{
_lightHeadStateUpdater.UpdateState(currentHeadState);
}
Logger.Debug($"Broadcast new state to clients '{JsonConvert.SerializeObject(currentLightState)}'.");
_hubContext.Clients.All.StateChanged(currentLightState);
return currentLightState;
}
这是一个打开灯处理程序,我们将新值写入 modbus:
protected override Response Handle(TurnOn request)
{
_lastCommandExecutionTimeContainer.SetLastCommandExecutionTime(DateTime.Now);
if (request.HeadId <= 0
|| !_state.Heads.Any(x=>x.UniqueId == request.HeadId))
{
return ResponseFactory.FromError(Error.NotExist);
}
var headState = _state.Heads.Single(x=>x.UniqueId == request.HeadId);
if (headState.IsTurnedOn)
return ResponseFactory.FromError(Error.AlreadyAsRequested);
_modbusMaster.WriteSingleRegister((byte)request.HeadId,IgnisRegistry.On.Value,0x0001);
headState.IsTurnedOn = true;
if (_state.CameraState != null &&
_ignisLightConfiguration.CameraHeadId.HasValue &&
_ignisLightConfiguration.CameraHeadId.Value == request.HeadId)
{
_state.CameraState.IsTurnedOn = true;
}
Logger.Trace($"Turn head lamp {request.HeadId} on.");
_hubContext.Clients.All.HeadStateChanged(headState);
return ResponseFactory.Success;
}