Improvements for async device management and exclusive access to device (#376)
This commit is contained in:
Родитель
833aeb3fb8
Коммит
c5b0e01fde
|
@ -0,0 +1,213 @@
|
|||
# EditorConfig for Visual Studio 2022: https://learn.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2022
|
||||
|
||||
# This is a top-most .editorconfig file
|
||||
root = true
|
||||
|
||||
#=====================================================
|
||||
#
|
||||
# nanoFramework specific settings
|
||||
#
|
||||
#
|
||||
#=====================================================
|
||||
[*]
|
||||
# Generic EditorConfig settings
|
||||
end_of_line = crlf
|
||||
charset = utf-8-bom
|
||||
|
||||
# Visual Studio spell checker
|
||||
spelling_languages = en-us
|
||||
spelling_checkable_types = strings,identifiers,comments
|
||||
spelling_error_severity = information
|
||||
spelling_exclusion_path = spelling_exclusion.dic
|
||||
|
||||
#=====================================================
|
||||
#
|
||||
# Settings copied from the .NET runtime
|
||||
#
|
||||
# https://github.com/dotnet/runtime
|
||||
#
|
||||
#=====================================================
|
||||
# Default settings:
|
||||
# A newline ending every file
|
||||
# Use 4 spaces as indentation
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Generated code
|
||||
[*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}]
|
||||
generated_code = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
|
||||
# Modifier preferences
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
|
||||
|
||||
# avoid this. unless absolutely necessary
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
|
||||
# Types: use keywords instead of BCL types, and permit var only when the type is clear
|
||||
csharp_style_var_for_built_in_types = false:suggestion
|
||||
csharp_style_var_when_type_is_apparent = false:none
|
||||
csharp_style_var_elsewhere = false:suggestion
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# name all constant fields using PascalCase
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# static fields should have s_ prefix
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
|
||||
dotnet_naming_symbols.static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_fields.required_modifiers = static
|
||||
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
|
||||
dotnet_naming_style.static_prefix_style.required_prefix = s_
|
||||
dotnet_naming_style.static_prefix_style.capitalization = camel_case
|
||||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
|
||||
# Code style defaults
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
dotnet_sort_system_directives_first = true
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_preserve_single_line_blocks = true:none
|
||||
csharp_preserve_single_line_statements = false:none
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_prefer_simple_using_statement = false:none
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_collection_expression = when_types_exactly_match
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = true:silent
|
||||
csharp_style_expression_bodied_constructors = true:silent
|
||||
csharp_style_expression_bodied_operators = true:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = true:silent
|
||||
|
||||
# Pattern matching
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
|
||||
# Null checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Other features
|
||||
csharp_style_prefer_index_operator = false:none
|
||||
csharp_style_prefer_range_operator = false:none
|
||||
csharp_style_pattern_local_over_anonymous_function = false:none
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# License header
|
||||
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
# C++ Files
|
||||
[*.{cpp,h,in}]
|
||||
curly_bracket_next_line = true
|
||||
indent_brace_style = Allman
|
||||
|
||||
# Xml project files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{csproj,vbproj,proj,nativeproj,locproj}]
|
||||
charset = utf-8
|
||||
|
||||
# Xml build files
|
||||
[*.builds]
|
||||
indent_size = 2
|
||||
|
||||
# Xml files
|
||||
[*.{xml,stylecop,resx,ruleset}]
|
||||
indent_size = 2
|
||||
|
||||
# Xml config files
|
||||
[*.{props,targets,config,nuspec}]
|
||||
indent_size = 2
|
||||
|
||||
# YAML config files
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
# Shell scripts
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
[*.{cmd,bat}]
|
||||
end_of_line = crlf
|
|
@ -1,18 +1,20 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using nanoFramework.Tools.Debugger.PortComposite;
|
||||
using nanoFramework.Tools.Debugger.PortSerial;
|
||||
using nanoFramework.Tools.Debugger.PortTcpIp;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger
|
||||
{
|
||||
// write intellisense documentation for this class
|
||||
public abstract partial class PortBase : PortMessageBase
|
||||
{
|
||||
protected PortBase()
|
||||
{
|
||||
NanoFrameworkDevices = NanoFrameworkDevices.Instance;
|
||||
}
|
||||
|
||||
#region creating serial instances
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger
|
||||
|
@ -10,7 +13,13 @@ namespace nanoFramework.Tools.Debugger
|
|||
|
||||
public static NanoFrameworkDevices Instance
|
||||
{
|
||||
get { return _instance.Value; }
|
||||
get
|
||||
{
|
||||
lock (_instance)
|
||||
{
|
||||
return _instance.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NanoFrameworkDevices() { }
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using nanoFramework.Tools.Debugger.PortTcpIp;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger.NFDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// Code that wants to access a device should use this system-wide exclusive access while
|
||||
/// communicating to a device to prevent that another nanoFramework tool also wants to
|
||||
/// communicate with the device.
|
||||
/// </summary>
|
||||
public static class GlobalExclusiveDeviceAccess
|
||||
{
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// Base name for the system-wide mutex that controls access to a device connected to a COM port.
|
||||
/// </summary>
|
||||
private const string MutexBaseName = "276545121198496AADD346A60F14EF8D_";
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Communicate with a serial device and ensure the code to be executed as exclusive access to the device.
|
||||
/// </summary>
|
||||
/// <param name="serialPort">The serial port the device is connected to.</param>
|
||||
/// <param name="communication">Code to execute while having exclusive access to the device</param>
|
||||
/// <param name="millisecondsTimeout">Maximum time in milliseconds to wait for exclusive access</param>
|
||||
/// <param name="cancellationToken">Cancellation token that can be cancelled to stop/abort running the <paramref name="communication"/>.
|
||||
/// This method does not stop/abort execution of <paramref name="communication"/> after it has been started.</param>
|
||||
/// <returns>Indicates whether the <paramref name="communication"/> has been executed. Returns <c>false</c> if exclusive access
|
||||
/// cannot be obtained within <paramref name="millisecondsTimeout"/>, or if <paramref name="cancellationToken"/> was cancelled
|
||||
/// before the <paramref name="communication"/> has been started.</returns>
|
||||
public static bool CommunicateWithDevice(string serialPort, Action communication, int millisecondsTimeout = Timeout.Infinite, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
return DoCommunicateWithDevice(serialPort, communication, millisecondsTimeout, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Communicate with a device accessible via the network and ensure the code to be executed as exclusive access to the device.
|
||||
/// </summary>
|
||||
/// <param name="address">The network address the device is connected to.</param>
|
||||
/// <param name="communication">Code to execute while having exclusive access to the device</param>
|
||||
/// <param name="millisecondsTimeout">Maximum time in milliseconds to wait for exclusive access</param>
|
||||
/// <param name="cancellationToken">Cancellation token that can be cancelled to stop/abort running the <paramref name="communication"/>.
|
||||
/// This method does not stop/abort execution of <paramref name="communication"/> after it has been started.</param>
|
||||
/// <returns>Indicates whether the <paramref name="communication"/> has been executed. Returns <c>false</c> if exclusive access
|
||||
/// cannot be obtained within <paramref name="millisecondsTimeout"/>, or if <paramref name="cancellationToken"/> was cancelled
|
||||
/// before the <paramref name="communication"/> has been started.</returns>
|
||||
public static bool CommunicateWithDevice(NetworkDeviceInformation address, Action communication, int millisecondsTimeout = Timeout.Infinite, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
return DoCommunicateWithDevice($"{address.Host}:{address.Port}", communication, millisecondsTimeout, cancellationToken);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
private static bool DoCommunicateWithDevice(string connectionKey, Action communication, int millisecondsTimeout, CancellationToken? cancellationToken)
|
||||
{
|
||||
for (bool retry = true; retry;)
|
||||
{
|
||||
retry = false;
|
||||
|
||||
var waitHandles = new List<WaitHandle>();
|
||||
var mutex = new Mutex(false, $"{MutexBaseName}{connectionKey}");
|
||||
waitHandles.Add(mutex);
|
||||
|
||||
CancellationTokenSource timeOutToken = null;
|
||||
if (millisecondsTimeout > 0 && millisecondsTimeout != Timeout.Infinite)
|
||||
{
|
||||
timeOutToken = new CancellationTokenSource(millisecondsTimeout);
|
||||
waitHandles.Add(timeOutToken.Token.WaitHandle);
|
||||
}
|
||||
|
||||
if (cancellationToken.HasValue)
|
||||
{
|
||||
waitHandles.Add(cancellationToken.Value.WaitHandle);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (WaitHandle.WaitAny(waitHandles.ToArray()) == 0)
|
||||
{
|
||||
communication();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (AbandonedMutexException)
|
||||
{
|
||||
// While this process is waiting on a mutex, the process that owned the mutex has been terminated
|
||||
// without properly releasing the mutex.
|
||||
// Try again, if this is the only remaining process it will re-create the mutex and get exclusive access.
|
||||
retry = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
mutex.ReleaseMutex();
|
||||
timeOutToken?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
@ -20,8 +19,6 @@ namespace nanoFramework.Tools.Debugger.PortComposite
|
|||
IEnumerable<PortBase> ports,
|
||||
bool startDeviceWatchers = true)
|
||||
{
|
||||
NanoFrameworkDevices = NanoFrameworkDevices.Instance;
|
||||
|
||||
_ports.AddRange(ports);
|
||||
|
||||
SubscribeToPortEvents();
|
||||
|
@ -52,21 +49,27 @@ namespace nanoFramework.Tools.Debugger.PortComposite
|
|||
|
||||
private void OnPortDeviceEnumerationCompleted(object sender, EventArgs e)
|
||||
{
|
||||
DeviceEnumerationCompleted?.Invoke(this, EventArgs.Empty);
|
||||
IsDevicesEnumerationComplete = (from p in _ports
|
||||
where p.IsDevicesEnumerationComplete
|
||||
select p).Any();
|
||||
if (IsDevicesEnumerationComplete)
|
||||
{
|
||||
DeviceEnumerationCompleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <exception cref="NotImplementedException">This API is not available in </exception>
|
||||
public override void AddDevice(string deviceId)
|
||||
/// <exception cref="NotImplementedException">This API is not available in PortCompositeDeviceManager.</exception>
|
||||
public override NanoDeviceBase AddDevice(string deviceId)
|
||||
{
|
||||
_ports.ForEach(p =>
|
||||
{
|
||||
p.AddDevice(deviceId);
|
||||
});
|
||||
// None of the Port*Manager has a check whether deviceId matches the ID handled by the manager,
|
||||
// so we don't know how to add a device here.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void StartDeviceWatchers()
|
||||
{
|
||||
IsDevicesEnumerationComplete = false;
|
||||
_ports.ForEach(p => p.StartDeviceWatchers());
|
||||
}
|
||||
|
||||
|
@ -77,6 +80,7 @@ namespace nanoFramework.Tools.Debugger.PortComposite
|
|||
|
||||
public override void ReScanDevices()
|
||||
{
|
||||
IsDevicesEnumerationComplete = false;
|
||||
Task.Run(() =>
|
||||
{
|
||||
_ports.ForEach(p => p.ReScanDevices());
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
|
@ -9,6 +7,12 @@ namespace nanoFramework.Tools.Debugger
|
|||
{
|
||||
public interface IPort
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Instance ID of the port that is unique among all ports
|
||||
/// (regardless of the type of port).
|
||||
/// </summary>
|
||||
string InstanceId { get; }
|
||||
|
||||
int AvailableBytes { get; }
|
||||
|
||||
int SendBuffer(byte[] buffer);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -52,13 +50,15 @@ namespace nanoFramework.Tools.Debugger
|
|||
/// </summary>
|
||||
public bool IsDevicesEnumerationComplete { get; internal set; } = false;
|
||||
|
||||
public NanoFrameworkDevices NanoFrameworkDevices { get; protected set; }
|
||||
public NanoFrameworkDevices NanoFrameworkDevices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new <see cref="PortSerial"/> device to list of NanoFrameworkDevices.
|
||||
/// Adds a new device to list of NanoFrameworkDevices.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The serial port name where the device is connected.</param>
|
||||
public abstract void AddDevice(string deviceId);
|
||||
/// <param name="deviceId">The unique ID (based on the connection properties) of the device.</param>
|
||||
/// <returns>The device with the unique ID that is added or (if it was already discovered before) retrieved
|
||||
/// from the list of devices. Returns <see langword="null"/> if no device has been added.</returns>
|
||||
public abstract NanoDeviceBase AddDevice(string deviceId);
|
||||
|
||||
/// <summary>
|
||||
/// Starts the device watchers.
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
using Microsoft.Win32;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using nanoFramework.Tools.Debugger.NFDevice;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger.PortSerial
|
||||
{
|
||||
|
@ -43,6 +49,17 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// </summary>
|
||||
public event EventDeviceRemoved Removed;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a delegate method that is used to handle the AllNewDevicesAdded event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
public delegate void EventAllNewDevicesAdded(object sender);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all newly discovered devices have been added
|
||||
/// </summary>
|
||||
public event EventAllNewDevicesAdded AllNewDevicesAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status of the device watcher.
|
||||
/// </summary>
|
||||
|
@ -60,13 +77,15 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// <summary>
|
||||
/// Starts the device watcher.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
/// <param name="portsToExclude">The collection of serial ports to ignore when searching for devices.
|
||||
/// Changes in the collection after the start of the device watcher are taken into account.</param>
|
||||
public void Start(ICollection<string> portsToExclude = null)
|
||||
{
|
||||
if (!_started)
|
||||
{
|
||||
_threadWatch = new Thread(() =>
|
||||
{
|
||||
StartWatcher();
|
||||
StartWatcher(portsToExclude ?? []);
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
|
@ -77,21 +96,30 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
}
|
||||
}
|
||||
|
||||
private void StartWatcher()
|
||||
private void StartWatcher(ICollection<string> portsToExclude)
|
||||
{
|
||||
_ownerManager.OnLogMessageAvailable($"PortSerial device watcher started @ Thread {_threadWatch.ManagedThreadId} [ProcessID: {Process.GetCurrentProcess().Id}]");
|
||||
|
||||
_ports = new List<string>();
|
||||
_ports = [];
|
||||
|
||||
_started = true;
|
||||
|
||||
int newPortsDetected = 0;
|
||||
var newPortsDetectedLock = new object();
|
||||
|
||||
Status = DeviceWatcherStatus.Started;
|
||||
|
||||
while (_started)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ports = GetPortNames();
|
||||
var ports = new List<string>();
|
||||
lock (portsToExclude)
|
||||
{
|
||||
ports.AddRange(from p in GetPortNames()
|
||||
where !portsToExclude.Contains(p)
|
||||
select p);
|
||||
}
|
||||
|
||||
// check for ports that departed
|
||||
List<string> portsToRemove = new();
|
||||
|
@ -120,10 +148,36 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
if (!_ports.Contains(port))
|
||||
{
|
||||
_ports.Add(port);
|
||||
Added?.Invoke(this, port);
|
||||
if (Added is not null)
|
||||
{
|
||||
if (PortSerialManager.GetRegisteredDevice(port) is null)
|
||||
{
|
||||
lock (newPortsDetectedLock)
|
||||
{
|
||||
newPortsDetected++;
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// Force true async running
|
||||
await Task.Yield();
|
||||
GlobalExclusiveDeviceAccess.CommunicateWithDevice(
|
||||
port,
|
||||
() => Added.Invoke(this, port)
|
||||
);
|
||||
lock (newPortsDetectedLock)
|
||||
{
|
||||
if (--newPortsDetected == 0)
|
||||
{
|
||||
AllNewDevicesAdded?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
#if DEBUG
|
||||
|
@ -145,17 +199,16 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// <summary>
|
||||
/// Gets the list of serial ports.
|
||||
/// </summary>
|
||||
/// <returns>The list of serial ports.</returns>
|
||||
public List<string> GetPortNames()
|
||||
/// <returns>The list of serial ports that may be connected to a nanoDevice.</returns>
|
||||
public static List<string> GetPortNames()
|
||||
{
|
||||
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? GetPortNames_Linux()
|
||||
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? GetPortNames_OSX()
|
||||
: RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")) ? GetPortNames_FreeBSD()
|
||||
: GetPortNames_Windows();
|
||||
}
|
||||
|
||||
private List<string> GetPortNames_Linux()
|
||||
private static List<string> GetPortNames_Linux()
|
||||
{
|
||||
List<string> ports = new List<string>();
|
||||
|
||||
|
@ -176,7 +229,7 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
return ports;
|
||||
}
|
||||
|
||||
private List<string> GetPortNames_OSX()
|
||||
private static List<string> GetPortNames_OSX()
|
||||
{
|
||||
List<string> ports = new List<string>();
|
||||
|
||||
|
@ -213,7 +266,7 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
return ports;
|
||||
}
|
||||
|
||||
private List<string> GetPortNames_FreeBSD()
|
||||
private static List<string> GetPortNames_FreeBSD()
|
||||
{
|
||||
List<string> ports = new List<string>();
|
||||
|
||||
|
@ -236,13 +289,49 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
return ports;
|
||||
}
|
||||
|
||||
private List<string> GetPortNames_Windows()
|
||||
private static List<string> GetPortNames_Windows()
|
||||
{
|
||||
const string FindFullPathPattern = @"\\\\\?\\([\w]*)#([\w&]*)#([\w&]*)";
|
||||
const string RegExPattern = @"\\Device\\([a-zA-Z]*)(\d)";
|
||||
List<string> portNames = new List<string>();
|
||||
try
|
||||
{
|
||||
// discard known system and other rogue devices
|
||||
bool IsSpecialPort(string deviceFullPath)
|
||||
{
|
||||
if (deviceFullPath is not null)
|
||||
{
|
||||
// make it upper case for comparison
|
||||
string deviceFULLPATH = deviceFullPath.ToUpperInvariant();
|
||||
|
||||
if (
|
||||
deviceFULLPATH.StartsWith(@"\\?\ACPI") ||
|
||||
|
||||
// reported in https://github.com/nanoframework/Home/issues/332
|
||||
// COM ports from Broadcom 20702 Bluetooth adapter
|
||||
deviceFULLPATH.Contains(@"VID_0A5C+PID_21E1") ||
|
||||
|
||||
// reported in https://nanoframework.slack.com/archives/C4MGGBH1P/p1531660736000055?thread_ts=1531659631.000021&cid=C4MGGBH1P
|
||||
// COM ports from Broadcom 20702 Bluetooth adapter
|
||||
deviceFULLPATH.Contains(@"VID&00010057_PID&0023") ||
|
||||
|
||||
// reported in Discord channel
|
||||
deviceFULLPATH.Contains(@"VID&0001009E_PID&400A") ||
|
||||
|
||||
// this seems to cover virtual COM ports from Bluetooth devices
|
||||
deviceFULLPATH.Contains("BTHENUM") ||
|
||||
|
||||
// this seems to cover virtual COM ports by ELTIMA
|
||||
deviceFULLPATH.Contains("EVSERIAL")
|
||||
)
|
||||
{
|
||||
// don't even bother with this one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gets the list of supposed open ports
|
||||
RegistryKey allPorts = Registry.LocalMachine.OpenSubKey(@"HARDWARE\DEVICEMAP\SERIALCOMM");
|
||||
RegistryKey deviceFullPaths = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\COM Name Arbiter\Devices");
|
||||
|
@ -271,7 +360,8 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
if (portKeyInfo != null)
|
||||
{
|
||||
string portName = (string)allPorts.GetValue(port);
|
||||
if (portName != null)
|
||||
if (portName != null
|
||||
&& !IsSpecialPort((string)deviceFullPaths.GetValue(portName)))
|
||||
{
|
||||
portNames.Add(portName);
|
||||
}
|
||||
|
@ -281,29 +371,32 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
else
|
||||
{
|
||||
string portName = (string)allPorts.GetValue(port);
|
||||
if (portName != null)
|
||||
string deviceFullPath = (string)deviceFullPaths.GetValue(portName);
|
||||
if (deviceFullPath != null)
|
||||
{
|
||||
// Get the full qualified name of the device
|
||||
string deviceFullPath = (string)deviceFullPaths.GetValue(portName);
|
||||
if (deviceFullPath != null)
|
||||
if (IsSpecialPort(deviceFullPath))
|
||||
{
|
||||
var devicePathDetail = Regex.Match(deviceFullPath.Replace("+", "&"), FindFullPathPattern);
|
||||
if ((devicePathDetail.Success) && (devicePathDetail.Groups.Count == 4))
|
||||
{
|
||||
string devicePath = deviceFullPath.Split('#')[1];
|
||||
// don't even bother with this one
|
||||
continue;
|
||||
}
|
||||
|
||||
RegistryKey device = Registry.LocalMachine.OpenSubKey($"SYSTEM\\CurrentControlSet\\Enum\\{devicePathDetail.Groups[1]}\\{devicePath}\\{devicePathDetail.Groups[3]}");
|
||||
if (device != null)
|
||||
// Get the full qualified name of the device
|
||||
var devicePathDetail = Regex.Match(deviceFullPath.Replace("+", "&"), FindFullPathPattern);
|
||||
if ((devicePathDetail.Success) && (devicePathDetail.Groups.Count == 4))
|
||||
{
|
||||
string devicePath = deviceFullPath.Split('#')[1];
|
||||
|
||||
RegistryKey device = Registry.LocalMachine.OpenSubKey($"SYSTEM\\CurrentControlSet\\Enum\\{devicePathDetail.Groups[1]}\\{devicePath}\\{devicePathDetail.Groups[3]}");
|
||||
if (device != null)
|
||||
{
|
||||
string service = (string)device.GetValue("Service");
|
||||
if (service != null)
|
||||
{
|
||||
string service = (string)device.GetValue("Service");
|
||||
if (service != null)
|
||||
activePorts = Registry.LocalMachine.OpenSubKey($"SYSTEM\\CurrentControlSet\\Services\\{service}\\Enum");
|
||||
if (activePorts != null)
|
||||
{
|
||||
activePorts = Registry.LocalMachine.OpenSubKey($"SYSTEM\\CurrentControlSet\\Services\\{service}\\Enum");
|
||||
if (activePorts != null)
|
||||
{
|
||||
// If the device is still plugged, it should appear as valid here, if not present, it means, the device has been disconnected
|
||||
portNames.Add(portName);
|
||||
}
|
||||
// If the device is still plugged, it should appear as valid here, if not present, it means, the device has been disconnected
|
||||
portNames.Add(portName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Win32;
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
@ -14,6 +9,8 @@ using System.IO.Ports;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger.PortSerial
|
||||
{
|
||||
|
@ -27,10 +24,6 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
// counter of device watchers completed
|
||||
private int _deviceWatchersCompletedCount = 0;
|
||||
|
||||
// counter of device watchers completed
|
||||
private int _newDevicesCount = 0;
|
||||
private readonly object _newDevicesCountLock = new object();
|
||||
|
||||
private readonly Random _delay = new Random(DateTime.Now.Millisecond);
|
||||
|
||||
private readonly ConcurrentDictionary<string, CachedDeviceInfo> _devicesCache = new ConcurrentDictionary<string, CachedDeviceInfo>();
|
||||
|
@ -40,9 +33,12 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// <summary>
|
||||
/// Creates an Serial debug client
|
||||
/// </summary>
|
||||
/// <param name="startDeviceWatchers">Indicates whether to start the device watcher.</param>
|
||||
/// <param name="portExclusionList">The collection of serial ports to ignore when searching for devices.
|
||||
/// Changes in the collection after the start of the device watcher are taken into account.</param>
|
||||
/// <param name="bootTime"></param>
|
||||
public PortSerialManager(bool startDeviceWatchers = true, List<string> portExclusionList = null, int bootTime = 3000)
|
||||
{
|
||||
NanoFrameworkDevices = NanoFrameworkDevices.Instance;
|
||||
_deviceWatcher = new(this);
|
||||
|
||||
BootTime = bootTime;
|
||||
|
@ -66,8 +62,6 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
|
||||
public override void ReScanDevices()
|
||||
{
|
||||
_newDevicesCount = 0;
|
||||
|
||||
// need to reset this here to have intimidate effect
|
||||
IsDevicesEnumerationComplete = false;
|
||||
|
||||
|
@ -101,6 +95,7 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
{
|
||||
_deviceWatcher.Added += OnDeviceAdded;
|
||||
_deviceWatcher.Removed += OnDeviceRemoved;
|
||||
_deviceWatcher.AllNewDevicesAdded += ProcessDeviceEnumerationComplete;
|
||||
}
|
||||
|
||||
public void StartSerialDeviceWatchers()
|
||||
|
@ -116,11 +111,10 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
{
|
||||
// Start all device watchers
|
||||
|
||||
_deviceWatcher.Start();
|
||||
_deviceWatcher.Start(PortExclusionList);
|
||||
|
||||
_watchersStarted = true;
|
||||
|
||||
_deviceWatchersCompletedCount = 0;
|
||||
IsDevicesEnumerationComplete = false;
|
||||
}
|
||||
|
||||
|
@ -146,8 +140,13 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
|
||||
private void NanoFrameworkDevicesRemoveAllSerial()
|
||||
{
|
||||
List<string> devicesToRemove;
|
||||
|
||||
// also clear nanoFramework devices list
|
||||
var devicesToRemove = NanoFrameworkDevices.Select(nanoDevice => ((NanoDevice<NanoSerialDevice>)nanoDevice).DeviceId).ToList();
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
devicesToRemove = NanoFrameworkDevices.Select(nanoDevice => ((NanoDevice<NanoSerialDevice>)nanoDevice).DeviceId).ToList();
|
||||
}
|
||||
|
||||
foreach (var deviceId in devicesToRemove)
|
||||
{
|
||||
|
@ -158,140 +157,153 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
#endregion
|
||||
|
||||
#region Methods to manage device list add, remove, etc
|
||||
/// <summary>
|
||||
/// Get the device that communicates via the serial port, provided it has been added to the
|
||||
/// list of known devices.
|
||||
/// </summary>
|
||||
/// <param name="portName">The port name of the device to get.</param>
|
||||
/// <returns>The <see cref="NanoDeviceBase"/> that communicates via the serial port, or <see langword="null"/> if the device is not found.</returns>
|
||||
public static NanoDeviceBase GetRegisteredDevice(string portName)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(portName))
|
||||
{
|
||||
var devices = NanoFrameworkDevices.Instance;
|
||||
lock (devices)
|
||||
{
|
||||
return devices.FirstOrDefault(d => (d as NanoDevice<NanoSerialDevice>)?.DeviceId == portName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new <see cref="PortSerial"/> device to list of NanoFrameworkDevices.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The serial port name where the device is connected.</param>
|
||||
public override void AddDevice(string deviceId)
|
||||
/// <returns>The device with the unique ID that is added or (if it was already discovered before) retrieved
|
||||
/// from the list of devices. Returns <see langword="null"/> if no device has been added.</returns>
|
||||
public override NanoDeviceBase AddDevice(string deviceId)
|
||||
{
|
||||
AddDeviceToListAsync(deviceId);
|
||||
return AddDeviceToListAsync(deviceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="NanoDevice"/> and adds it to the list of devices.
|
||||
/// Creates a <see cref="NanoDevice{NanoSerialDevice}"/> and adds it to the list of devices.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The AQS used to find this device</param>
|
||||
private void AddDeviceToListAsync(string deviceId)
|
||||
private NanoDeviceBase AddDeviceToListAsync(string deviceId)
|
||||
{
|
||||
// search the nanoFramework device list for a device with a matching interface ID
|
||||
var nanoFrameworkDeviceMatch = FindNanoFrameworkDevice(deviceId);
|
||||
|
||||
// Add the device if it's new
|
||||
if (nanoFrameworkDeviceMatch == null)
|
||||
if (nanoFrameworkDeviceMatch is null)
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.CandidateDevice(deviceId));
|
||||
|
||||
if (nanoFrameworkDeviceMatch == null)
|
||||
// Create a new element for this device and...
|
||||
var newNanoFrameworkDevice = new NanoDevice<NanoSerialDevice>();
|
||||
newNanoFrameworkDevice.DeviceId = deviceId;
|
||||
newNanoFrameworkDevice.ConnectionPort = new PortSerial(this, newNanoFrameworkDevice);
|
||||
newNanoFrameworkDevice.Transport = TransportType.Serial;
|
||||
|
||||
var connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
{
|
||||
// Create a new element for this device and...
|
||||
var newNanoFrameworkDevice = new NanoDevice<NanoSerialDevice>();
|
||||
newNanoFrameworkDevice.DeviceId = deviceId;
|
||||
newNanoFrameworkDevice.ConnectionPort = new PortSerial(this, newNanoFrameworkDevice);
|
||||
newNanoFrameworkDevice.Transport = TransportType.Serial;
|
||||
|
||||
var connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(deviceId));
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
{
|
||||
if (CheckValidNanoFrameworkSerialDevice(newNanoFrameworkDevice))
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(deviceId));
|
||||
//add device to the collection
|
||||
NanoFrameworkDeviceAdd(newNanoFrameworkDevice);
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
nanoFrameworkDeviceMatch = newNanoFrameworkDevice;
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
else
|
||||
{
|
||||
if (CheckValidNanoFrameworkSerialDevice(newNanoFrameworkDevice))
|
||||
{
|
||||
//add device to the collection
|
||||
NanoFrameworkDeviceAdd(newNanoFrameworkDevice);
|
||||
// disconnect
|
||||
newNanoFrameworkDevice.Disconnect();
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
// devices powered by the USB cable and that feature a serial converter (like an FTDI chip)
|
||||
// are still booting when the USB enumeration event raises
|
||||
// so need to give them enough time for the boot sequence to complete before trying to communicate with them
|
||||
|
||||
// Failing to connect to debugger engine on first attempt occurs frequently on dual USB devices like ESP32 WROVER KIT.
|
||||
// Seems to be something related with both devices using the same USB endpoint
|
||||
// Another reason is that an ESP32 takes around 3 seconds to complete the boot sequence and launch the CLR.
|
||||
// Until then the device will look non responsive or invalid to the detection mechanism that we're using.
|
||||
// A nice workaround for this seems to be adding an extra random wait so the comms are not simultaneous.
|
||||
|
||||
int delay;
|
||||
lock (_delay)
|
||||
{
|
||||
delay = _delay.Next(200, 600);
|
||||
}
|
||||
else
|
||||
|
||||
Thread.Sleep(BootTime + delay);
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.CheckingValidDevice($" {newNanoFrameworkDevice.DeviceId} *** 2nd attempt ***"));
|
||||
|
||||
connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
{
|
||||
// disconnect
|
||||
newNanoFrameworkDevice.Disconnect();
|
||||
|
||||
// devices powered by the USB cable and that feature a serial converter (like an FTDI chip)
|
||||
// are still booting when the USB enumeration event raises
|
||||
// so need to give them enough time for the boot sequence to complete before trying to communicate with them
|
||||
|
||||
// Failing to connect to debugger engine on first attempt occurs frequently on dual USB devices like ESP32 WROVER KIT.
|
||||
// Seems to be something related with both devices using the same USB endpoint
|
||||
// Another reason is that an ESP32 takes around 3 seconds to complete the boot sequence and launch the CLR.
|
||||
// Until then the device will look non responsive or invalid to the detection mechanism that we're using.
|
||||
// A nice workaround for this seems to be adding an extra random wait so the comms are not simultaneous.
|
||||
|
||||
int delay;
|
||||
lock (_delay)
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(deviceId));
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
{
|
||||
if (CheckValidNanoFrameworkSerialDevice(newNanoFrameworkDevice, true))
|
||||
{
|
||||
delay = _delay.Next(200, 600);
|
||||
}
|
||||
NanoFrameworkDeviceAdd(newNanoFrameworkDevice);
|
||||
|
||||
Thread.Sleep(BootTime + delay);
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.CheckingValidDevice($" {newNanoFrameworkDevice.DeviceId} *** 2nd attempt ***"));
|
||||
|
||||
connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(deviceId));
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
{
|
||||
if (CheckValidNanoFrameworkSerialDevice(newNanoFrameworkDevice, true))
|
||||
{
|
||||
NanoFrameworkDeviceAdd(newNanoFrameworkDevice);
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(deviceId));
|
||||
}
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(deviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(deviceId));
|
||||
}
|
||||
|
||||
// subtract devices count
|
||||
lock (_newDevicesCountLock)
|
||||
{
|
||||
_newDevicesCount--;
|
||||
}
|
||||
|
||||
|
||||
// check if we are done processing arriving devices
|
||||
if (_newDevicesCount == 0)
|
||||
{
|
||||
ProcessDeviceEnumerationComplete();
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(deviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(deviceId));
|
||||
}
|
||||
}
|
||||
return nanoFrameworkDeviceMatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// add device to the collection (if new)
|
||||
/// Adds a device to the collection (if new).
|
||||
/// </summary>
|
||||
/// <param name="newNanoFrameworkDevice">new NanoSerialDevice</param>
|
||||
/// <param name="newNanoFrameworkDevice">The new <see cref="NanoSerialDevice"/></param>
|
||||
private void NanoFrameworkDeviceAdd(NanoDevice<NanoSerialDevice> newNanoFrameworkDevice)
|
||||
{
|
||||
if (newNanoFrameworkDevice != null && NanoFrameworkDevices.OfType<NanoDevice<NanoSerialDevice>>().Count(i => i.DeviceId == newNanoFrameworkDevice.DeviceId) == 0)
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
//add device to the collection
|
||||
NanoFrameworkDevices.Add(newNanoFrameworkDevice);
|
||||
if (newNanoFrameworkDevice != null && NanoFrameworkDevices.OfType<NanoDevice<NanoSerialDevice>>().Count(i => i.DeviceId == newNanoFrameworkDevice.DeviceId) == 0)
|
||||
{
|
||||
//add device to the collection
|
||||
NanoFrameworkDevices.Add(newNanoFrameworkDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DisposeDevice(string instanceId)
|
||||
{
|
||||
var deviceToDispose = NanoFrameworkDevices.FirstOrDefault(nanoDevice => ((NanoDevice<NanoSerialDevice>)nanoDevice).DeviceId == instanceId);
|
||||
NanoDeviceBase deviceToDispose;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
deviceToDispose = NanoFrameworkDevices.FirstOrDefault(nanoDevice => ((NanoDevice<NanoSerialDevice>)nanoDevice).DeviceId == instanceId);
|
||||
}
|
||||
|
||||
if (deviceToDispose != null)
|
||||
{
|
||||
|
@ -306,24 +318,46 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DeviceDeparture(deviceId));
|
||||
|
||||
// get devices and remove them from collection
|
||||
NanoFrameworkDevices.OfType<NanoDevice<NanoSerialDevice>>()
|
||||
.Where(i => i.DeviceId == deviceId).ToList()
|
||||
.ForEach(RemoveNanoFrameworkDevices);
|
||||
List<NanoDevice<NanoSerialDevice>> devices;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
// get devices
|
||||
devices = NanoFrameworkDevices.OfType<NanoDevice<NanoSerialDevice>>()
|
||||
.Where(i => i.DeviceId == deviceId).ToList();
|
||||
}
|
||||
|
||||
// remove them from collection
|
||||
devices.ForEach(RemoveNanoFrameworkDevices);
|
||||
}
|
||||
|
||||
private void RemoveNanoFrameworkDevices(NanoDevice<NanoSerialDevice> device)
|
||||
{
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
device?.DebugEngine?.StopProcessing();
|
||||
device?.DebugEngine?.Dispose();
|
||||
if (device is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
}
|
||||
|
||||
// get rid of debug engine, if that was created
|
||||
device.DebugEngine?.StopProcessing();
|
||||
device.DebugEngine?.Dispose();
|
||||
|
||||
// disconnect device in order to free port
|
||||
device.Disconnect(true);
|
||||
}
|
||||
|
||||
private NanoDeviceBase FindNanoFrameworkDevice(string deviceId)
|
||||
{
|
||||
if (deviceId != null)
|
||||
{
|
||||
return NanoFrameworkDevices.FirstOrDefault(d => (d as NanoDevice<NanoSerialDevice>).DeviceId == deviceId);
|
||||
lock (NanoFrameworkDevices.Instance)
|
||||
{
|
||||
return NanoFrameworkDevices.FirstOrDefault(d => (d as NanoDevice<NanoSerialDevice>)?.DeviceId == deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -333,7 +367,7 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// Remove the device from the device list
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="deviceInformationUpdate"></param>
|
||||
/// <param name="serialPort"></param>
|
||||
private void OnDeviceRemoved(object sender, string serialPort)
|
||||
{
|
||||
RemoveDeviceFromList(serialPort);
|
||||
|
@ -344,72 +378,27 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
/// This function will add the device to the listOfDevices
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="deviceInformation"></param>
|
||||
/// <param name="serialPort"></param>
|
||||
private void OnDeviceAdded(object sender, string serialPort)
|
||||
{
|
||||
// check against exclusion list
|
||||
if (PortExclusionList.Contains(serialPort))
|
||||
bool exclude;
|
||||
lock (PortExclusionList)
|
||||
{
|
||||
exclude = PortExclusionList.Contains(serialPort);
|
||||
}
|
||||
if (exclude)
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DroppingDeviceToExclude(serialPort));
|
||||
return;
|
||||
}
|
||||
|
||||
// discard known system and other rogue devices
|
||||
RegistryKey portKeyInfo = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\COM Name Arbiter\Devices");
|
||||
if (portKeyInfo != null)
|
||||
{
|
||||
var portInfo = (string)portKeyInfo.GetValue(serialPort);
|
||||
|
||||
if (portInfo != null)
|
||||
{
|
||||
Debug.WriteLine($"{nameof(OnDeviceAdded)}: port {serialPort}, portinfo: {portInfo}");
|
||||
|
||||
// make it upper case for comparison
|
||||
portInfo = portInfo.ToUpperInvariant();
|
||||
|
||||
if (
|
||||
portInfo.StartsWith(@"\\?\ACPI") ||
|
||||
|
||||
// reported in https://github.com/nanoframework/Home/issues/332
|
||||
// COM ports from Broadcom 20702 Bluetooth adapter
|
||||
portInfo.Contains(@"VID_0A5C+PID_21E1") ||
|
||||
|
||||
// reported in https://nanoframework.slack.com/archives/C4MGGBH1P/p1531660736000055?thread_ts=1531659631.000021&cid=C4MGGBH1P
|
||||
// COM ports from Broadcom 20702 Bluetooth adapter
|
||||
portInfo.Contains(@"VID&00010057_PID&0023") ||
|
||||
|
||||
// reported in Discord channel
|
||||
portInfo.Contains(@"VID&0001009E_PID&400A") ||
|
||||
|
||||
// this seems to cover virtual COM ports from Bluetooth devices
|
||||
portInfo.Contains("BTHENUM") ||
|
||||
|
||||
// this seems to cover virtual COM ports by ELTIMA
|
||||
portInfo.Contains("EVSERIAL")
|
||||
)
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DroppingDeviceToExclude(serialPort));
|
||||
|
||||
// don't even bother with this one
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DeviceArrival(serialPort));
|
||||
|
||||
lock (_newDevicesCountLock)
|
||||
{
|
||||
_newDevicesCount++;
|
||||
}
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Policy.Handle<InvalidOperationException>()
|
||||
.WaitAndRetry(10, retryCount => TimeSpan.FromMilliseconds((retryCount * retryCount) * 25),
|
||||
onRetry: (exception, delay, retryCount, context) => LogRetry(exception, delay, retryCount, context))
|
||||
.Execute(() => AddDeviceToListAsync(serialPort));
|
||||
});
|
||||
Policy.Handle<InvalidOperationException>()
|
||||
.WaitAndRetry(10, retryCount => TimeSpan.FromMilliseconds((retryCount * retryCount) * 25),
|
||||
onRetry: (exception, delay, retryCount, context) => LogRetry(exception, delay, retryCount, context))
|
||||
.Execute(() => AddDeviceToListAsync(serialPort));
|
||||
}
|
||||
|
||||
private void LogRetry(Exception exception, TimeSpan delay, object retryCount, object context)
|
||||
|
@ -425,12 +414,23 @@ namespace nanoFramework.Tools.Debugger.PortSerial
|
|||
|
||||
#region Handlers and events for Device Enumeration Complete
|
||||
|
||||
private void ProcessDeviceEnumerationComplete()
|
||||
private void ProcessDeviceEnumerationComplete(object sender)
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.SerialDeviceEnumerationCompleted(NanoFrameworkDevices.Count));
|
||||
int count;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
if (IsDevicesEnumerationComplete)
|
||||
{
|
||||
// Nothing has changed
|
||||
return;
|
||||
}
|
||||
// all watchers have completed enumeration
|
||||
IsDevicesEnumerationComplete = true;
|
||||
|
||||
// all watchers have completed enumeration
|
||||
IsDevicesEnumerationComplete = true;
|
||||
count = NanoFrameworkDevices.OfType<NanoDevice<NanoSerialDevice>>().Count();
|
||||
}
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.SerialDeviceEnumerationCompleted(count));
|
||||
|
||||
// fire event that Serial enumeration is complete
|
||||
OnDeviceEnumerationCompleted();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
@ -9,6 +7,8 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using nanoFramework.Tools.Debugger.NFDevice;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger.PortTcpIp
|
||||
{
|
||||
|
@ -148,7 +148,21 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
switch (command)
|
||||
{
|
||||
case CommandDeviceStart:
|
||||
Added?.Invoke(this, new NetworkDeviceInformation(host, port));
|
||||
if (Added is not null)
|
||||
{
|
||||
var info = new NetworkDeviceInformation(host, port);
|
||||
if (PortTcpIpManager.GetRegisteredDevice(info) is null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Yield(); // Force true async running
|
||||
GlobalExclusiveDeviceAccess.CommunicateWithDevice(info, () =>
|
||||
{
|
||||
Added.Invoke(this, info);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandDeviceStop:
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
@ -19,7 +17,7 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
|
||||
private NanoNetworkDevice NanoNetworkDevice => NanoDevice.Device;
|
||||
|
||||
private string InstanceId => NanoDevice.DeviceId;
|
||||
public string InstanceId => NanoDevice.DeviceId;
|
||||
|
||||
public override event EventHandler<StringEventArgs> LogMessageAvailable;
|
||||
|
||||
|
@ -140,4 +138,4 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
LogMessageAvailable?.Invoke(this, new StringEventArgs(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
@ -12,6 +8,8 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger.PortTcpIp
|
||||
{
|
||||
|
@ -27,13 +25,14 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
// Network device watchers started flag
|
||||
private bool _watchersStarted = false;
|
||||
|
||||
// counter of device watchers completed
|
||||
private int _newDevicesCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Internal list with the actual nF Network devices
|
||||
/// Internal list with the actual nF Network devices.
|
||||
/// This must be a static list as NanoFrameworkDevices is also global.
|
||||
/// Take care that all items of _networkDevices are in the NanoFrameworkDevices,
|
||||
/// and that there are no devices in NanoFrameworkDevices that should be present
|
||||
/// in _networkDevices but are not (and use NanoFrameworkDevices for locks).
|
||||
/// </summary>
|
||||
private readonly List<NetworkDeviceInformation> _networkDevices = new List<NetworkDeviceInformation>();
|
||||
private static readonly List<NetworkDeviceInformation> _networkDevices = new List<NetworkDeviceInformation>();
|
||||
|
||||
private IEnumerable<NanoDevice<NanoNetworkDevice>> _networkNanoFrameworkDevices =>
|
||||
NanoFrameworkDevices.Cast<NanoDevice<NanoNetworkDevice>>();
|
||||
|
@ -48,8 +47,6 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
{
|
||||
_deviceWatcher = new DeviceWatcher(this, discoveryPort);
|
||||
|
||||
NanoFrameworkDevices = NanoFrameworkDevices.Instance;
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
InitializeDeviceWatchers();
|
||||
|
@ -126,21 +123,40 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
}
|
||||
|
||||
// Clear the list of devices so we don't have potentially disconnected devices around
|
||||
ClearDeviceEntries();
|
||||
|
||||
// also clear nanoFramework devices list
|
||||
var devicesToRemove = _networkNanoFrameworkDevices.Select(nanoDevice => nanoDevice.DeviceId).ToList();
|
||||
List<string> devicesToRemove;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
devicesToRemove = _networkNanoFrameworkDevices.Select(nanoDevice => nanoDevice.DeviceId).ToList();
|
||||
}
|
||||
|
||||
foreach (var deviceId in devicesToRemove)
|
||||
{
|
||||
// get device...
|
||||
var device = FindNanoFrameworkDevice(deviceId);
|
||||
NanoDeviceBase device;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
var deviceEntry = FindDevice(deviceId);
|
||||
if (deviceEntry is null)
|
||||
{
|
||||
// this is not a TcpIp-connected device and is managed by another PortManager
|
||||
continue;
|
||||
}
|
||||
|
||||
// ... and remove it from collection
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
// ... and remove it from collection
|
||||
_networkDevices.Remove(deviceEntry);
|
||||
|
||||
device?.DebugEngine?.StopProcessing();
|
||||
device?.DebugEngine?.Stop(true);
|
||||
device = FindNanoFrameworkDevice(deviceId);
|
||||
if (device is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// ... and remove it from collection
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
}
|
||||
device.DebugEngine?.StopProcessing();
|
||||
device.DebugEngine?.Stop(true);
|
||||
}
|
||||
|
||||
_watchersStarted = false;
|
||||
|
@ -150,69 +166,91 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
|
||||
|
||||
#region Methods to manage device list add, remove, etc
|
||||
/// <summary>
|
||||
/// Get the device that communicates via the network port, provided it has been added to the
|
||||
/// list of known devices.
|
||||
/// </summary>
|
||||
/// <param name="networkDevice">The name of the network device.</param>
|
||||
/// <returns></returns>
|
||||
public static NanoDeviceBase GetRegisteredDevice(NetworkDeviceInformation networkDevice)
|
||||
{
|
||||
if (networkDevice is not null)
|
||||
{
|
||||
var devices = NanoFrameworkDevices.Instance;
|
||||
lock (devices)
|
||||
{
|
||||
return devices.FirstOrDefault(d => (d as NanoDevice<NanoNetworkDevice>)?.DeviceId == networkDevice.DeviceId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DeviceListEntry for a device and adds it to the list of devices
|
||||
/// </summary>
|
||||
private void AddDeviceToListAsync(NetworkDeviceInformation networkDevice)
|
||||
private (NanoDeviceBase device, bool isNew) AddDeviceToListAsync(NetworkDeviceInformation networkDevice)
|
||||
{
|
||||
bool isNew = false;
|
||||
|
||||
// search the device list for a device with a matching interface ID
|
||||
var networkMatch = FindDevice(networkDevice.DeviceId);
|
||||
NetworkDeviceInformation networkMatch;
|
||||
NanoDeviceBase nanoFrameworkDeviceMatch;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
networkMatch = FindDevice(networkDevice.DeviceId);
|
||||
|
||||
// search the nanoFramework device list for a device with a matching interface ID
|
||||
nanoFrameworkDeviceMatch = FindNanoFrameworkDevice(networkDevice.DeviceId);
|
||||
}
|
||||
|
||||
// Add the device if it's new
|
||||
if (networkMatch != null) return;
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.CandidateDevice(networkDevice.DeviceId));
|
||||
|
||||
// search the nanoFramework device list for a device with a matching interface ID
|
||||
var nanoFrameworkDeviceMatch = FindNanoFrameworkDevice(networkDevice.DeviceId);
|
||||
|
||||
if (nanoFrameworkDeviceMatch != null) return;
|
||||
|
||||
// Create a new element for this device and...
|
||||
var newNanoFrameworkDevice = new NanoDevice<NanoNetworkDevice>();
|
||||
newNanoFrameworkDevice.DeviceId = networkDevice.DeviceId;
|
||||
newNanoFrameworkDevice.ConnectionPort = new PortTcpIp(this, newNanoFrameworkDevice, networkDevice);
|
||||
newNanoFrameworkDevice.Transport = TransportType.TcpIp;
|
||||
|
||||
var connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
if (networkMatch is null && nanoFrameworkDeviceMatch is null)
|
||||
{
|
||||
OnLogMessageAvailable(
|
||||
NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(networkDevice.DeviceId));
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
{
|
||||
if (CheckValidNanoFrameworkNetworkDevice(newNanoFrameworkDevice))
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.CandidateDevice(networkDevice.DeviceId));
|
||||
|
||||
// Create a new element for this device and...
|
||||
var newNanoFrameworkDevice = new NanoDevice<NanoNetworkDevice>();
|
||||
newNanoFrameworkDevice.DeviceId = networkDevice.DeviceId;
|
||||
newNanoFrameworkDevice.ConnectionPort = new PortTcpIp(this, newNanoFrameworkDevice, networkDevice);
|
||||
newNanoFrameworkDevice.Transport = TransportType.TcpIp;
|
||||
|
||||
var connectResult = newNanoFrameworkDevice.ConnectionPort.ConnectDevice();
|
||||
|
||||
if (connectResult == ConnectPortResult.Unauthorized)
|
||||
{
|
||||
//add device to the collection
|
||||
NanoFrameworkDevices.Add(newNanoFrameworkDevice);
|
||||
|
||||
_networkDevices.Add(networkDevice);
|
||||
|
||||
OnLogMessageAvailable(
|
||||
NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
NanoDevicesEventSource.Log.UnauthorizedAccessToDevice(networkDevice.DeviceId));
|
||||
}
|
||||
else if (connectResult == ConnectPortResult.Connected)
|
||||
{
|
||||
if (CheckValidNanoFrameworkNetworkDevice(newNanoFrameworkDevice))
|
||||
{
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
//add device to the collection
|
||||
NanoFrameworkDevices.Add(newNanoFrameworkDevice);
|
||||
_networkDevices.Add(networkDevice);
|
||||
}
|
||||
|
||||
OnLogMessageAvailable(
|
||||
NanoDevicesEventSource.Log.ValidDevice($"{newNanoFrameworkDevice.Description}"));
|
||||
|
||||
nanoFrameworkDeviceMatch = newNanoFrameworkDevice;
|
||||
isNew = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// disconnect
|
||||
newNanoFrameworkDevice.Disconnect();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// disconnect
|
||||
newNanoFrameworkDevice.Disconnect();
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(networkDevice.DeviceId));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.QuitDevice(networkDevice.DeviceId));
|
||||
}
|
||||
|
||||
// subtract devices count
|
||||
_newDevicesCount--;
|
||||
|
||||
// check if we are done processing arriving devices
|
||||
if (_newDevicesCount == 0)
|
||||
{
|
||||
ProcessDeviceEnumerationComplete();
|
||||
}
|
||||
return (nanoFrameworkDeviceMatch, isNew);
|
||||
}
|
||||
|
||||
public override void DisposeDevice(string instanceId)
|
||||
|
@ -226,32 +264,39 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
private void RemoveDeviceFromList(NetworkDeviceInformation networkDevice)
|
||||
{
|
||||
// Removes the device entry from the internal list; therefore the UI
|
||||
var deviceEntry = FindDevice(networkDevice.DeviceId);
|
||||
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DeviceDeparture(networkDevice.DeviceId));
|
||||
|
||||
_networkDevices.Remove(deviceEntry);
|
||||
|
||||
// get device...
|
||||
var device = FindNanoFrameworkDevice(networkDevice.DeviceId);
|
||||
NanoDeviceBase device;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
var deviceEntry = FindDevice(networkDevice.DeviceId);
|
||||
if (deviceEntry != null)
|
||||
{
|
||||
_networkDevices.Remove(deviceEntry);
|
||||
}
|
||||
|
||||
// ... and remove it from collection
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
device = FindNanoFrameworkDevice(networkDevice.DeviceId);
|
||||
if (device != null)
|
||||
{
|
||||
// ... and remove it from collection
|
||||
NanoFrameworkDevices.Remove(device);
|
||||
}
|
||||
}
|
||||
|
||||
// get rid of debug engine, if that was created
|
||||
device?.DebugEngine?.StopProcessing();
|
||||
device?.DebugEngine?.Dispose();
|
||||
}
|
||||
|
||||
private void ClearDeviceEntries()
|
||||
{
|
||||
_networkDevices.Clear();
|
||||
// disconnect device
|
||||
device?.Disconnect(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches through the existing list of devices for the first DeviceListEntry that has
|
||||
/// the specified device Id.
|
||||
/// </summary>
|
||||
internal NetworkDeviceInformation FindDevice(string deviceId) =>
|
||||
private NetworkDeviceInformation FindDevice(string deviceId) =>
|
||||
_networkDevices.FirstOrDefault(d => d.DeviceId == deviceId);
|
||||
|
||||
private NanoDeviceBase FindNanoFrameworkDevice(string deviceId) =>
|
||||
|
@ -272,9 +317,12 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
{
|
||||
OnLogMessageAvailable(NanoDevicesEventSource.Log.DeviceArrival(networkDevice.DeviceId));
|
||||
|
||||
_newDevicesCount++;
|
||||
var (_, isNew) = AddDeviceToListAsync(networkDevice);
|
||||
|
||||
Task.Run(() => { AddDeviceToListAsync(networkDevice); });
|
||||
if (isNew && !IsDevicesEnumerationComplete)
|
||||
{
|
||||
ProcessDeviceEnumerationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -284,11 +332,16 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
|
||||
private void ProcessDeviceEnumerationComplete()
|
||||
{
|
||||
OnLogMessageAvailable(
|
||||
NanoDevicesEventSource.Log.SerialDeviceEnumerationCompleted(NanoFrameworkDevices.Count));
|
||||
int count;
|
||||
lock (NanoFrameworkDevices)
|
||||
{
|
||||
IsDevicesEnumerationComplete = true;
|
||||
count = NanoFrameworkDevices.OfType<NanoDevice<NanoNetworkDevice>>().Count();
|
||||
}
|
||||
|
||||
// all watchers have completed enumeration
|
||||
IsDevicesEnumerationComplete = true;
|
||||
// TODO: count are not serial devices
|
||||
OnLogMessageAvailable(
|
||||
NanoDevicesEventSource.Log.SerialDeviceEnumerationCompleted(count));
|
||||
|
||||
// fire event that Network enumeration is complete
|
||||
OnDeviceEnumerationCompleted();
|
||||
|
@ -494,7 +547,8 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
LogMessageAvailable?.Invoke(this, new StringEventArgs(message));
|
||||
}
|
||||
|
||||
public override void AddDevice(string deviceId)
|
||||
/// <inheritdoc/>
|
||||
public override NanoDeviceBase AddDevice(string deviceId)
|
||||
{
|
||||
// expected format is "tcpip://{Host}:{Port}"
|
||||
|
||||
|
@ -504,9 +558,9 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
throw new ArgumentException("Invalid tcpip format.");
|
||||
}
|
||||
|
||||
AddDeviceToListAsync(new NetworkDeviceInformation(
|
||||
return AddDeviceToListAsync(new NetworkDeviceInformation(
|
||||
match.Groups["host"].Value,
|
||||
int.Parse(match.Groups["port"].Value)));
|
||||
int.Parse(match.Groups["port"].Value))).device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -517,4 +571,4 @@ namespace nanoFramework.Tools.Debugger.PortTcpIp
|
|||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using nanoFramework.Tools.Debugger.Extensions;
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
@ -16,6 +10,9 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using nanoFramework.Tools.Debugger.Extensions;
|
||||
using nanoFramework.Tools.Debugger.WireProtocol;
|
||||
using Polly;
|
||||
|
||||
namespace nanoFramework.Tools.Debugger
|
||||
{
|
||||
|
@ -77,6 +74,14 @@ namespace nanoFramework.Tools.Debugger
|
|||
|
||||
internal Engine(NanoDeviceBase device)
|
||||
{
|
||||
lock (_syncReqLockForPort)
|
||||
{
|
||||
if (!_syncReqLockForPort.TryGetValue(device.ConnectionPort.InstanceId, out _syncReqLock))
|
||||
{
|
||||
_syncReqLockForPort[device.ConnectionPort.InstanceId] = _syncReqLock = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
InitializeLocal(device);
|
||||
|
||||
// default to false
|
||||
|
@ -619,9 +624,10 @@ namespace nanoFramework.Tools.Debugger
|
|||
|
||||
/// <summary>
|
||||
/// Global lock object for synchronizing message request. This ensures there is only one
|
||||
/// outstanding request at any point of time.
|
||||
/// outstanding request per device at any point of time.
|
||||
/// </summary>
|
||||
private static readonly SemaphoreSlim _syncReqLock = new SemaphoreSlim(1, 1);
|
||||
private readonly static Dictionary<string, SemaphoreSlim> _syncReqLockForPort = [];
|
||||
private readonly SemaphoreSlim _syncReqLock;
|
||||
|
||||
private IncomingMessage PerformSyncRequest(
|
||||
uint command,
|
||||
|
@ -821,42 +827,42 @@ namespace nanoFramework.Tools.Debugger
|
|||
switch (bp.Cmd)
|
||||
{
|
||||
case Commands.c_Monitor_Ping:
|
||||
{
|
||||
// signal that a monitor ping was received
|
||||
_pingEvent.Set();
|
||||
|
||||
Commands.Monitor_Ping.Reply cmdReply = new Commands.Monitor_Ping.Reply
|
||||
{
|
||||
// signal that a monitor ping was received
|
||||
_pingEvent.Set();
|
||||
Source = Commands.Monitor_Ping.c_Ping_Source_Host,
|
||||
Flags = (StopDebuggerOnConnect ? Commands.Monitor_Ping.c_Ping_DbgFlag_Stop : 0)
|
||||
};
|
||||
|
||||
Commands.Monitor_Ping.Reply cmdReply = new Commands.Monitor_Ping.Reply
|
||||
{
|
||||
Source = Commands.Monitor_Ping.c_Ping_Source_Host,
|
||||
Flags = (StopDebuggerOnConnect ? Commands.Monitor_Ping.c_Ping_DbgFlag_Stop : 0)
|
||||
};
|
||||
PerformRequestAsync(
|
||||
new OutgoingMessage(
|
||||
_controlller.GetNextSequenceId(),
|
||||
message,
|
||||
_controlller.CreateConverter(),
|
||||
Flags.c_NonCritical,
|
||||
cmdReply)
|
||||
);
|
||||
|
||||
PerformRequestAsync(
|
||||
new OutgoingMessage(
|
||||
_controlller.GetNextSequenceId(),
|
||||
message,
|
||||
_controlller.CreateConverter(),
|
||||
Flags.c_NonCritical,
|
||||
cmdReply)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Commands.c_Monitor_Message:
|
||||
{
|
||||
Commands.Monitor_Message payload = message.Payload as Commands.Monitor_Message;
|
||||
|
||||
Debug.Assert(payload != null);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
Commands.Monitor_Message payload = message.Payload as Commands.Monitor_Message;
|
||||
|
||||
Debug.Assert(payload != null);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
Task.Factory.StartNew(() => _eventMessage?.Invoke(message, payload.ToString()));
|
||||
}
|
||||
|
||||
return true;
|
||||
Task.Factory.StartNew(() => _eventMessage?.Invoke(message, payload.ToString()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case Commands.c_Debugging_Messaging_Query:
|
||||
Debug.Assert(message.Payload != null);
|
||||
Task.Factory.StartNew(() => RpcReceiveQuery(message, (Commands.Debugging_Messaging_Query)message.Payload));
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)NetworkInformation\Wireless80211_ConfigurationOptions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NetworkInformation\RadioType.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NFDevice\CachedDeviceInfo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NFDevice\GlobalExclusiveDeviceAccess.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NFDevice\NanoDeviceBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NFDevice\INanoDevice.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NFDevice\NanoDevice.cs" />
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
nano
|
Загрузка…
Ссылка в новой задаче