/// <summary> /// Асинхронное пакетное чтение множества преременных. /// </summary> /// <param name="options">Параметры NiBUS-операции.</param> /// <param name="target">Адрес устойства.</param> /// <param name="ids">Список идентификаторов переменных.</param> /// <returns> /// Аснихронная задача /// </returns> /// <seealso cref="ReadProgressInfo"/> public async Task ReadManyValuesAsync(NibusOptions options, Address target, params int[] ids) { //Contract.Requires(options != null); //Contract.Requires(options.Progress != null); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); foreach (var splitIds in ids.Split(NmsMessage.NmsMaxDataLength / 3).Select(splitIds => splitIds.ToArray())) { NmsMessage lastMessage; IncomingMessages.TryReceive(null, out lastMessage); await OutgoingMessages.SendAsync(new NmsReadMany(target, splitIds)); var tasks = from id in splitIds select GetNmsReadResponseAsync(lastMessage, target, id, options) .ContinueWith( task => { var rp = new ReadProgressInfo(target, id, task); if (Logger.IsTraceEnabled) { Logger.Trace("Received id={0}, Exc = {1}", rp.Id, rp.Exception); } options.Progress.Report(rp); }); try { await Task.WhenAll(tasks.ToArray()); } catch (Exception e) { Logger.Error(e, "Read failed"); } } }
/// <summary> /// Асинхронная проверка доступности устройства. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns> /// Возвращает время в миллисекундах, затрачиваемое на отправку запроса /// и получение соответствующего сообщения ответа. /// </returns> /// <value> /// <c>-1</c> - устройство не ответило в течение заданного интервала времени <see cref="NibusOptions.Timeout"/> /// либо выполнение было прервано пользователем или статус ответа содержал ошибку. /// </value> public async Task <long> PingAsync(Address target, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var queryVersion = new NmsRead(target, (int)StdNms.SoftwareVersion); var sw = new Stopwatch(); try { sw.Start(); await WaitForNmsResponseAsync(queryVersion, options).ConfigureAwait(false); sw.Stop(); } catch (TimeoutException) { return(-1); } catch (TaskCanceledException) { return(-1); } catch (NibusResponseException) { return(-1); } return(sw.ElapsedMilliseconds); }
private void LastMessageDublicate_ThrowTimeout() { var nmsProtocol = _stack.GetCodec <NmsCodec>().Protocol; var options = new NibusOptions(); NmsMessage lastMessage; nmsProtocol.IncomingMessages.TryReceive(null, out lastMessage); var query = new NmsRead(Destanation, 2); nmsProtocol.OutgoingMessages.Post(query); var wob = new WriteOnceBlock <NmsMessage>(m => m); NmsMessage response, response2; using (nmsProtocol.IncomingMessages.LinkTo( wob, m => !ReferenceEquals(m, lastMessage) && m.IsResponse && m.ServiceType == query.ServiceType && m.Id == query.Id)) { response = wob.Receive(options.Timeout); } nmsProtocol.IncomingMessages.TryReceive(null, out lastMessage); Assert.That(lastMessage, Is.EqualTo(response)); var wob2 = new WriteOnceBlock <NmsMessage>(m => m); using (nmsProtocol.IncomingMessages.LinkTo( wob2, m => !ReferenceEquals(m, lastMessage) && m.IsResponse && m.ServiceType == query.ServiceType && m.Id == query.Id)) { response2 = wob2.Receive(options.Timeout); } }
// ReSharper disable MemberCanBePrivate.Global #region Сервис NmsServiceType.Read /// <summary> /// Проверка доступности устройства. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns> /// Возвращает время в миллисекундах, затрачиваемое на отправку запроса /// и получение соответствующего сообщения ответа. /// </returns> /// <value> /// <c>-1</c> - устройство не ответило в течение заданного интервала времени <see cref="NibusOptions.Timeout"/> /// либо выполнение было прервано пользователем или статус ответа содержал ошибку. /// </value> public long Ping(Address target, NibusOptions options = null) { Contract.Requires(!IsDisposed); Contract.Requires(target != null); Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); return(PingAsync(target, options).Result); }
/// <summary> /// The default Constructor. /// </summary> /// <param name="incoming">Источник входящих NMS-сообщений.</param> /// <param name="outgoing">Канал для исходящих NMS-сообщений.</param> internal NmsProtocol(IReceivableSourceBlock <NmsMessage> incoming, ITargetBlock <NmsMessage> outgoing) { IsDisposed = false; _cts = new CancellationTokenSource(); _defaultOptions = new NibusOptions { Token = _cts.Token }; IncomingMessages = incoming; OutgoingMessages = outgoing; if (SynchronizationContext.Current == null) { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); } var ui = TaskScheduler.FromCurrentSynchronizationContext(); var infoHandlers = new ActionBlock <NmsMessage>( message => OnInformationReport((NmsInformationReport)message), new ExecutionDataflowBlockOptions { //CancellationToken = _cts.Token, TaskScheduler = ui }); IncomingMessages.LinkTo( infoHandlers, message => message.ServiceType == NmsServiceType.InformationReport); }
/// <summary> /// Асинхронно остановить устройство с подтверждением успеха. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns><see cref="Task"/> - асинхронная операция.</returns> public async Task ShutdownDeviceComfirmedAsync(Address target, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var shutdown = new NmsShutdown(Address.Empty, target); await WaitForNmsResponseAsync(shutdown, options); }
/// <summary> /// Асинхронно продолжить выполнять подпрограмму на устройстве с подтверждением успеха операции. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="id">Идентификатор подпрограммы.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns><see cref="Task"/> - асинхронная операция.</returns> public async Task ResumeProgramComfirmedAsync(Address target, int id, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var resume = new NmsResume(Address.Empty, target, id); await WaitForNmsResponseAsync(resume, options); }
/// <summary> /// Асинхронное чтение переменной. /// </summary> /// <param name="target">Адрес устройства, с которого необходимо получить значение переменной.</param> /// <param name="id">Идентификатор переменной.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns><see cref="Task{TResult}"/> представляющий асинхронную операцию чтения.</returns> /// <seealso cref="WaitForNmsResponseAsync{TMessage}"/> public async Task <object> ReadValueAsync(Address target, int id, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var query = new NmsRead(target, id); var response = await WaitForNmsResponseAsync(query, options); return(response.Value); }
private NibusOptions GetDefaultOrClone(NibusOptions options) { return(options == null ? _defaultOptions : new NibusOptions { Attempts = options.Attempts, Progress = options.Progress, Timeout = options.Timeout, Token = options.Token }); }
/// <summary> /// Асинхронная загрузка массива данных в устройство. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="domain">Домен.</param> /// <param name="data">Данные.</param> /// <param name="offset">Смещение в домене.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns> /// <see cref="Task"/> - асинхронная операция. /// </returns> public async Task DownloadDomainAsync( Address target, string domain, byte[] data, uint offset, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); //Contract.Requires(domain != null); //Contract.Requires(0 < domain.Length && domain.Length <= 8); var query = new NmsRequestDomainDownload(Address.Empty, target, domain); var response = await WaitForNmsResponseAsync(query, options); if (data.Length + offset > response.DomainSize) { throw new ArgumentOutOfRangeException( "data", data.Length + offset, String.Format("Address space overflow detected. Domain size is {0} bytes.", response.DomainSize)); } var downloadOptions = new NibusOptions(options); downloadOptions.Attempts = Math.Max(downloadOptions.Attempts, 3); if (downloadOptions.Timeout < MinUploadDounloadTimeout) { downloadOptions.Timeout = MinUploadDounloadTimeout; } var initiation = new NmsInitiateDownloadSequence(Address.Empty, target, response.Id); await WaitForNmsResponseAsync(initiation, downloadOptions); foreach (var split in data.Split(16).Select(s => s.ToArray())) { downloadOptions.Token.ThrowIfCancellationRequested(); var download = new NmsDownloadSegment( Address.Empty, target, response.Id, offset, split, !response.IsFastDownload); if (response.IsFastDownload) { await OutgoingMessages.SendAsync(download); } else { await WaitForNmsResponseAsync(download, downloadOptions); } offset += (uint)split.Length; } }
/// <summary> /// Асинхронно начать выполнять подпрограмму на устройстве с подтверждением успеха операции. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="id">Идентификатор подпрограммы.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <param name="args">Аргументы, передаваемые в подпрограмму в виде массива пар, /// где первый элемент пары содержит тип параметра, второй - значение.</param> /// <returns><see cref="Task"/> - асинхронная операция.</returns> public async Task StartProgramConfirmedAsync( Address target, int id, NibusOptions options = null, params Tuple <NmsValueType, object>[] args) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var start = new NmsStartProgramInvocation(Address.Empty, target, id, true, args); await WaitForNmsResponseAsync(start, options); }
/// <summary> /// Разрешить сигнализацию о событии с подтверждением о приеме. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="id">Идентификатор события.</param> /// <param name="isEventEnabled"><c>true</c> - разрешить сигнализацию о событии, иначе - <c>false</c>.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns><see cref="Task"/> - асинхронная операция.</returns> public async Task EnableEventMonitoringConfirmedAsync( Address target, int id, bool isEventEnabled, NibusOptions options = null) { Contract.Requires(!IsDisposed); Contract.Requires(target != null); Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); var msg = new NmsAlterEventConditionMonitoring(Address.Empty, target, id, isEventEnabled); await WaitForNmsResponseAsync(msg, options); }
public void NmsProtocol_Ping_MinusOne() { var nmsProtocol = _stack.GetCodec <NmsCodec>().Protocol; var sw = new Stopwatch(); var options = new NibusOptions { Timeout = TimeSpan.FromSeconds(1) }; sw.Start(); Assert.That(nmsProtocol.Ping(Address.CreateMac(1, 2, 3, 4, 5, 6), options), Is.EqualTo(-1)); sw.Stop(); Assert.That(sw.ElapsedMilliseconds, Is.InRange(options.Timeout.TotalMilliseconds, options.Timeout.TotalMilliseconds + 150)); }
public void NmsProtocol_ReadMany() { var nmsProtocol = _stack.GetCodec <NmsCodec>().Protocol; var ids = GetMibIds(@"Z:\mibs\siolynx.mib.xsd"); Assert.That(ids.Length, Is.GreaterThanOrEqualTo(16)); var test = new List <ReadProgressInfo>(ids.Length); var options = new NibusOptions { Progress = new Progress <object>(pi => test.Add((ReadProgressInfo)pi)) }; nmsProtocol.ReadManyValuesAsync(options, Destanation, ids).Wait(); Assert.That(test.Select(pi => pi.IsFaulted), Is.All.EqualTo(false)); Assert.That(test.Count, Is.EqualTo(ids.Length)); }
/// <summary> /// Асинхронно изменить значение переменной с подтверждением успеха внесения изменений. /// </summary> /// <param name="target">Адрес устройства, на котором требуется изменить значение переменной.</param> /// <param name="id">Идентификатор переменной.</param> /// <param name="valueType">Тип значения.</param> /// <param name="value">Записываемое значение.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns> /// <see cref="Task"/> асинхронная операция. /// </returns> public async Task WriteValueConfirmedAsync( Address target, int id, NmsValueType valueType, object value, NibusOptions options = null) { Contract.Requires(!IsDisposed); Contract.Requires(target != null); Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); Contract.Requires(value != null); var write = new NmsWrite(target, id, valueType, value); await WaitForNmsResponseAsync(write, options); }
private void ReadValueAsync_ThrowTimeout() { var nmsProtocol = _stack.GetCodec <NmsCodec>().Protocol; var options = new NibusOptions { Attempts = 5, Timeout = TimeSpan.FromSeconds(1) }; try { nmsProtocol.ReadValueAsync(Address.CreateMac(1, 2, 3, 4, 5, 6), 2, options).Wait(); } catch (AggregateException e) { throw e.Flatten().InnerException; } }
static void Upload() { using (var stack = new NibusStack( new SerialTransport("COM7", 115200, true), new NibusDataCodec(), new NmsCodec())) { var nmsProtocol = stack.GetCodec <NmsCodec>().Protocol; try { var sw = new Stopwatch(); var total = 0; var options = new NibusOptions { Progress = new Progress <object>( cb => { if (total == 0) { total = (int)cb; Console.WriteLine("Total: {0}", total); } else { Console.Write("{0}\t{1}\r", cb, (int)cb * 100 / total); } }) }; sw.Start(); var result = nmsProtocol.UploadDomainAsync(Address.CreateMac(0x6c, 0xea), "NVRAM", 0, 0, options).Result; sw.Stop(); Console.WriteLine("Duration: {0}", sw.Elapsed); using (var file = File.Create(@"c:\temp\nvram.hex")) { file.Write(result, 0, result.Length); } } catch (AggregateException e) { Console.WriteLine(e.Flatten().InnerException); } } }
private async Task <NmsRead> GetNmsReadResponseAsync( NmsMessage lastMessage, Address target, int id, NibusOptions options) { var wob = new WriteOnceBlock <NmsMessage>(m => m); using (IncomingMessages.LinkTo( wob, m => !ReferenceEquals(lastMessage, m) && m.IsResponse && m.ServiceType == NmsServiceType.Read && m.Id == id && (target == Address.Empty || m.Datagram.Source == target))) { var response = (NmsRead)await wob.ReceiveAsync(options.Timeout, options.Token).ConfigureAwait(false); if (response.ErrorCode != 0) { throw new NibusResponseException(response.ErrorCode); } return(response); } }
/// <summary> /// Загружает с универсального пульта длительность таймеров и обновляет провайдера игры. /// </summary> /// <param name="uconsole">Адрес пульта или <c>null</c>.</param> /// <remarks>Асинхронная операция.</remarks> public async void LoadProvider(Address uconsole = null) { if (uconsole == null) { uconsole = Address.Empty; } var timerInfos = new List <ReadProgressInfo>(12); var options = new NibusOptions { Progress = new Progress <object>(o => timerInfos.Add((ReadProgressInfo)o)) }; await NmsProtocol.ReadManyValuesAsync( options, uconsole, (int)Uconsole.Timer1Min, (int)Uconsole.Timer1Sec, (int)Uconsole.Timer2Min, (int)Uconsole.Timer2Sec, (int)Uconsole.Timer3Min, (int)Uconsole.Timer3Sec, (int)Uconsole.Timer4Min, (int)Uconsole.Timer4Sec, (int)Uconsole.Timer5Min, (int)Uconsole.Timer5Sec, (int)Uconsole.Timer6Min, (int)Uconsole.Timer6Sec); foreach (var progressInfo in timerInfos.Where(pi => !pi.IsFaulted && !pi.IsCanceled)) { switch (progressInfo.Id) { case (int)Uconsole.Timer1Min: Provider.Timers[0].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer1Sec: Provider.Timers[0].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; case (int)Uconsole.Timer2Min: Provider.Timers[1].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer2Sec: Provider.Timers[1].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; case (int)Uconsole.Timer3Min: Provider.Timers[2].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer3Sec: Provider.Timers[2].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; case (int)Uconsole.Timer4Min: Provider.Timers[3].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer4Sec: Provider.Timers[3].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; case (int)Uconsole.Timer5Min: Provider.Timers[4].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer5Sec: Provider.Timers[4].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; case (int)Uconsole.Timer6Min: Provider.Timers[5].Duration = (uint)(Provider.Timers[0].Duration % 60 + (byte)progressInfo.Value * 60); break; case (int)Uconsole.Timer6Sec: Provider.Timers[5].Duration = (Provider.Timers[0].Duration / 60) * 60 + (byte)progressInfo.Value; break; } } }
/// <summary> /// Отправляет NMS-сообщение <paramref name="query"/> и ожидает ответа. /// Асинхронная операция. /// </summary> /// <typeparam name="TMessage">Тип сообщения, потомок NmsMessage.</typeparam> /// <param name="query">Сообщение-запрос.</param> /// <param name="options">Параметры асинхронной операции.</param> /// <returns> /// <see cref="Task{TResult}"/> асинхронная операция - ответное сообщение. /// </returns> /// <exception cref="AggregateException">Призошла ошибка в асинхронном коде. См. <see cref="Exception.InnerException"/></exception> /// <exception cref="TimeoutException">Ошибка по таймауту.</exception> /// <exception cref="TaskCanceledException">Операция прервана пользователем.</exception> /// <exception cref="NibusResponseException">Ошибка NiBUS.</exception> public async Task <TMessage> WaitForNmsResponseAsync <TMessage>(TMessage query, NibusOptions options = null) where TMessage : NmsMessage { //Contract.Requires(!IsDisposed); //Contract.Requires( // query.Datagram.Destanation.Type == AddressType.Hardware // || query.Datagram.Destanation.Type == AddressType.Net); options = GetDefaultOrClone(options); // Последнее сообщение в BroadcastBlock всегда остается! Незабываем его фильтровать. NmsMessage lastMessage; IncomingMessages.TryReceive(null, out lastMessage); var wob = new WriteOnceBlock <NmsMessage>(m => m); using (IncomingMessages.LinkTo( wob, m => !ReferenceEquals(lastMessage, m) && m.IsResponse && m.ServiceType == query.ServiceType && (query.Id == 0 || m.Id == query.Id) && (query.Datagram.Destanation == Address.Empty || m.Datagram.Source == query.Datagram.Destanation))) { for (var i = 0; i < options.Attempts; i++) { await OutgoingMessages.SendAsync(query); try { var response = (TMessage)await wob.ReceiveAsync(options.Timeout, options.Token).ConfigureAwait(false); if (response.ErrorCode != 0) { throw new NibusResponseException(response.ErrorCode); } return(response); } catch (TimeoutException) { Logger.Debug("Timeout {0}", i + 1); if (i < options.Attempts - 1) { continue; } throw; } } } // Эта точка недостижима при attempts > 0! throw new InvalidOperationException(); }
/// <summary> /// Асинхронная выгрузка массива данных с устройства. /// </summary> /// <param name="target">Адрес устройства.</param> /// <param name="domain">Домен.</param> /// <param name="offset">Смещение в домене.</param> /// <param name="cbSize">Количество байт.</param> /// <param name="options">Параметры NiBUS-операции.</param> /// <returns> /// <see cref="Task"/> - асинхронная операция с данными домена. /// </returns> public async Task <byte[]> UploadDomainAsync( Address target, string domain, uint offset = 0, uint cbSize = 0, NibusOptions options = null) { //Contract.Requires(!IsDisposed); //Contract.Requires(target != null); //Contract.Requires(target.Type == AddressType.Hardware || target.Type == AddressType.Net); //Contract.Requires(domain != null); //Contract.Requires(0 < domain.Length && domain.Length <= 8); options = GetDefaultOrClone(options); var query = new NmsRequestDomainUpload(Address.Empty, target, domain); var response = await WaitForNmsResponseAsync(query, options); if (cbSize > response.DomainSize - offset) { throw new ArgumentOutOfRangeException( "cbSize", cbSize, String.Format("Address space overflow detected. Domain size is {0} bytes.", response.DomainSize)); } if (cbSize == 0) { cbSize = response.DomainSize - offset; } if (options != null && options.Progress != null) { options.Progress.Report((int)cbSize); } var uploadOptions = new NibusOptions(options); uploadOptions.Attempts = Math.Max(uploadOptions.Attempts, 3); if (uploadOptions.Timeout < MinUploadDounloadTimeout) { uploadOptions.Timeout = MinUploadDounloadTimeout; } var initiation = new NmsInitiateUploadSequence(Address.Empty, target, response.Id); await WaitForNmsResponseAsync(initiation, uploadOptions); var uploaded = new List <byte>((int)cbSize); while (uploaded.Count < cbSize) { uploadOptions.Token.ThrowIfCancellationRequested(); var rest = cbSize - uploaded.Count; var upload = new NmsUploadSegment( Address.Empty, target, response.Id, offset + (uint)uploaded.Count, rest > 58 ? (byte)58 : (byte)rest); var respUpload = await WaitForNmsResponseAsync(upload, uploadOptions); if (respUpload.Offset != uploaded.Count + offset) { if (Logger.IsDebugEnabled) { Logger.Debug( "Invalid offset, resp.ofs = {0}, but = {1}", respUpload.Offset, uploaded.Count + offset); } continue; } uploaded.AddRange(respUpload.Segment); if (uploadOptions.Progress != null) { uploadOptions.Progress.Report(uploaded.Count); } } return(uploaded.ToArray()); }