chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//            Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.Linq;
using System.Threading;
using System.Text;
 
namespace OpenTap
{
    /// <summary>  
    /// Implements device address discovery for VISA Keysight instruments and searches for device aliases. 
    /// </summary>
    class VisaDeviceDiscovery : IDeviceDiscovery
    {
 
        static bool IsVisaDiscoveryDisabled()
        {
            var env = Environment.GetEnvironmentVariable("OPENTAP_NO_VISA_DISCOVERY");
            if (env == null) 
                return false;
 
            return string.Equals(env, "True",StringComparison.OrdinalIgnoreCase) || string.Equals(env, "1");
        }
        
        static readonly bool isDisabled = IsVisaDiscoveryDisabled();
        
        private static string[] GetDeviceAddresses()
        {
            var rm = GetResourceManager();
            if (rm == Visa.VI_NULL) return new string[0];
            int search, cnt;
            StringBuilder sb = new StringBuilder(1024);
 
            if (Visa.viFindRsrc(rm, "?*", out search, out cnt, sb) >= Visa.VI_SUCCESS)
            {
                string[] res = new string[cnt];
 
                if (cnt > 0)
                    res[0] = sb.ToString();
 
                int i = 1;
                while ((cnt-- > 0) && (Visa.viFindNext(search, sb) == Visa.VI_SUCCESS))
                {
                    res[i++] = sb.ToString();
                }
 
                if (i < res.Length)
                    Array.Resize(ref res, i);
 
                Visa.viClose(search);
 
                return res;
            }
 
            return new string[0];
        }
 
        
        static bool getAliasesBroken = false;
        static string[] getAliases(int rm, string address)
        {
            if (getAliasesBroken) return Array.Empty<string>();
            using (var semaphore = new Semaphore(0, 1))
            {
                string[] result = Array.Empty<string>();
                
                TapThread.Start(() =>
                {
                    var aliases = new StringBuilder(1024);
                    short intfType = 0;
                    short intfNum = 0;
                    if (Visa.viParseRsrcEx(rm, address, ref intfType, ref intfNum, null, null, aliases) ==
                        Visa.VI_SUCCESS)
                    {
                        if (aliases.Length != 0)
                            result = new[] {aliases.ToString()};
                    }
 
                    semaphore.Release();
                });
 
                if (!semaphore.WaitOne(5000))
                {
                    getAliasesBroken = true;
                    log.Warning("VISA timed out when trying to get aliases. Skipping this from now on.");
                }
 
                return result;
            }
        }
 
        /// <summary> Ensures updating device addresses is run from the same thread.</summary>
        static WorkQueue detectAddressQueue = new WorkQueue(WorkQueue.Options.None, nameof(VisaDeviceDiscovery));
        static string[] deviceAddresses = null; // not null after first detect.
        static void updateDeviceAddresses()
        {
            try
            {
                var baseAddresses = GetDeviceAddresses();
 
                // Now expand aliases
                var rm = GetResourceManager();
                if (rm == Visa.VI_NULL)
                {
                    deviceAddresses = baseAddresses;
                }
                else
                {
                    deviceAddresses = baseAddresses.SelectMany(addr => getAliases(rm, addr)).Concat(baseAddresses).ToArray();
                }
            }
            catch
            {
                deviceAddresses = Array.Empty<string>();
            }
        }
        static TraceSource log = Log.CreateSource(nameof(VisaDeviceDiscovery));
        public string[] DetectDeviceAddresses(DeviceAddressAttribute AddressType)
        {
            if (isDisabled) return Array.Empty<string>();
            using (var wait = new ManualResetEvent(false))
            {
                bool doUpdate = detectAddressQueue.QueueSize == 0;
                detectAddressQueue.EnqueueWork(() =>
                {
                    if (doUpdate)
                        updateDeviceAddresses();
                    if (!wait.SafeWaitHandle.IsClosed)
                        wait.Set();
                });
                if (!wait.WaitOne(deviceAddresses == null ? 2000 : 100))
                {
                    // updateDeviceAddresses can hang forever in case of an error in the VISA installation.
                    // in this case wait for a bit and return no result. Consequent times wait a very short time.
                    Utils.ErrorOnce(log, log, "Detecting device addresses took longer than expected. This might be caused by a broken VISA installation.");
                }
                return deviceAddresses ?? Array.Empty<string>();
            }
        }
 
        /// <summary>  Returns true if DeviceAddress is a VISA address. </summary>
        /// <param name="DeviceAddress">. </param>
        /// <returns> </returns>
        public bool CanDetect(DeviceAddressAttribute DeviceAddress)
        {
            if (isDisabled) return false;
            
            return (DeviceAddress is VisaAddressAttribute);
        }
 
        static int visa_resource;
        static bool visa_tried_load;
 
 
        // Required by .NET to catch AccessViolationException.
        [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] 
        internal static int GetResourceManager()
        {
            try
            {
                if (visa_resource == Visa.VI_NULL && !visa_tried_load)
                    RaiseError2(Visa.viOpenDefaultRM(out visa_resource));
            }
            catch (Exception)
            {
                visa_tried_load = true;
            }
            return visa_resource;
        }
 
        private static void RaiseError2(int error)
        {
            if (error < 0)
                throw new Exception("Visa failed with error " + error);
        }
    }
}