Triamec.Acquisitions示例代码

$Id: readme.txt 25205 2017-07-19 14:45:48Z chm $
Copyright ?2012 Triamec Motion AG

Caution: you may harm your hardware when executing sample applications 
without adjusting configuration values to your hardware environment.
Please read and follow the recommendations below
before executing any sample application.


Overview
--------

The acquisition sample is a .NET windows forms application demonstrating how to
record data from your devices using the Triamec Automation and Motion software 
development kit (TAM SDK).

Note that the acquisitions namespace is in a tested beta state. Be prepared that the API may still change in a newer
version.

While the interesting code is concentrated in 8 methods of class AcquisitionForm,
you see a couple of other files and methods which contribute to 
- configuring your hardware environment, and
- creating a proper .NET windows forms application.


Hardware requirements
---------------------

- a Tria-Link PCI adapter (TL100, TLC201 or TLC100) mounted to your PC,
- at least one servo-drive TS151, TS351, TSP350 or TSP700 connected to the Tria-Link,
- a motor and encoder connected to the servo-drive,
- power supply for the servo-drive logic and motor power.


Software environment adjustment
-------------------------------

MSChart is used in order to visualize the recorded data. Before you can compile
this sample, please invoke the installer in the ext\MSChart directory, or
visit the official download page
http://www.microsoft.com/download/en/details.aspx?id=14422.


Hardware configuration adjustment
---------------------------------

The TAM configuration file HelloWorld.TAMcfg must be replaced by one
appropriate for your particular hardware environment.

It is recommended to use the TAM System Explorer (part of the TAM SDK)
as a convenient way to create your configuration file.
With the TAM System Explorer, you can
- boot and identify your system;
- manipulate the register values of your drives;
- save the TAM configuration as an XML file.

It is clear that the adjustment of register values is a major task 
and requires understanding of motion control.
Consult the TAM System Explorer Quick Start Guide or  
contact Triamec if you need further assistance with this setup procedure.
For this sample, a working TAM configuration file is a prerequisite.

As an alternative to replacing the content of the existing HelloWorld.TAMcfg,
you can 
- add your own TAM configuration file to the Acquisition project,
- make sure its "Copy to Output Directory" property is set to "Copy if newer",
  and
- change the application setting "TamConfigurationPath" to the name of your
  file. The application settings are found in the Properties folder as
  Settings.settings file.
  
If the drive you use is not a TS151, you need to change the code in the
AcquisitionForm.Startup method accordingly. If you use a TS150 or TS350 or
whatever drive controlling two axes, you must change all using statement
mentioning Rlid4 to Rlid5. The correct number is shown in the
TAM System Explorer in the general properties of the device as
Register Layout ID, or programmatically in the ITamDevice.RegisterLayoutId
property of a drive.


What the program does
---------------------

As soon as the AcquisitionForm is shown,
- a TAM topology is created,
- the Tria-Link gets booted,
- the TAM configuration gets loaded and applied,
- a TS151 servo-drive gets searched.

After this, the servo-drive is immediately enabled, and an endless move
forth-and-back sequence is started, while simultaneously recording data and
showing it in the chart. Data is recorded at the highest possible rate triggered
by the raising edge of the current velocity of the axis.

The user can
- change the level of the trigger.
- change the recording duration.

The program also demonstrates how the acquisitions framework allows to record
data while holding the UI responsive.


Shortcomings
------------

While MSChart is easy to distribute and configure to a professional look&feel,
it is not the big shot according performance as needed in signal processing.
If you need to plot lots of data, as possible with the scope of the TAM System
Explorer, consider to choose a product like National Instruments Measurement
Studio .NET.

No exception handling code has been provided. Many of the methods of
the TAM API actually may throw exceptions due to communication loss, which you
should handle. Intellisense shows what exception types a method may throw. The
TAM API documentation gives more information about the method and when it throws
exceptions.

In the sample code, WaitForTermination method is always called with no
arguments. However, there is an overload of the WaitForTermination method
accepting a timeout argument. In order to guard your application from being
blocked in case of communication loss, you should typically pass a timeout and
handle the case where WaitForTermination returns false.

No exception is thrown if, for example, a move failed because of an axis error.
In a more elaborate application, you must therefore do something like the following:
  
  TamRequest request = this.axis.MoveRelative(1);
  if (!request.WaitForTermination(1000) || (request.Termination != TamRequestResolution.Completed)) {
    // exception handling
  }

You may notice that the movements are sometimes delayed. This behavior is
inherent to .NET and the Windows operating system. You may be able to reduce
peak delays by elevating process and/or thread priorities and making use of
GC.Collect, but you won't get any guarantees about the maximum delay.
If you have a situation where code needs to be executed in some limited period
of time, consider to write a custom firmware extension by means of a Tama
program (see the Tama Library sample), or employ a real time system like
TwinCAT.

// Copyright © 2012 Triamec Motion AG

using System;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using Triamec.Acquisitions;
using Triamec.Tam.Acquisitions;
using Triamec.Tam.Registers;
using Triamec.Tam.Samples.Properties;
using Triamec.Tam.UI;
using Triamec.TriaLink;
using Axis = Triamec.Tam.Rlid4.Axis;

namespace Triamec.Tam.Samples {
    internal sealed partial class AcquisitionForm : Form {
        #region Fields
        private TamTopology _topology;
        private ITamDrive _drive;
        private TamAxis _axis;
        private ITamAcquisition _acquisition;
        private ITamVariable<double> _positionVariable, _positionErrorVariable;
        private ITamTrigger _trigger;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="AcquisitionForm"/> class.
        /// </summary>
        public AcquisitionForm() {
            InitializeComponent();
        }
        #endregion Constructor

        #region Acquisition demo code
        /// <summary>
        /// Prepares the TAM system.
        /// </summary>
        /// <remarks>
        ///     <list type="bullet">
        ///         <item><description>Creates a TAM topology,</description></item>
        ///         <item><description>boots the Tria-Link,</description></item>
        ///         <item><description>searches for a TS151 servo-drive,</description></item>
        ///         <item><description>loads and applies a TAM configuration,</description></item>
        ///         <item><description>creates the acquisition.</description></item>
        ///     </list>
        /// </remarks>
        /// <exception cref="TamException">Startup failed.</exception>
        private void Startup() {

            // Create the root object representing the topology of the TAM hardware.
            // Note that we must dispose this object at the end in order to clean up resources.
            _topology = new TamTopology("Tutorial");

            // Add the local TAM system on this PC to the topology.
            var system = _topology.AddLocalTamSystem(null);

            // Get the (first) Tria-Link on the (first) PCI Adapter of the local TAM system.
            var link = system[0][0];

            // Boot the Tria-Link so that it learns about connected stations.
            // Note that you may use the more robust Initialize() when the Tria-Link adapter isn't an observer
            // (see link.Adapter.Role).
            link.Identify();

            // Find the (first) TS151 drive in the Tria-Link.
            // Iterate over the stations one by one
            // because the Tria-Link booting does not guarantee a particular order.
            foreach (var station in link) {
                if (station.HardwareIdDetails.ProductType == ProductType.FromName("TS151")) {

                    // found a drive to work with
                    _drive = station.Device as ITamDrive;

                    break; // out of foreach loop
                }
            }
            if (_drive == null) throw new TamException("Drive not found.");

            // Load a TAM configuration using a GUI.
            // Alternatively, an instance of the Triamec.Tam.Configuration.Deserializer class can be
            // instantiated, giving more possibilities.
            LoadSurveyor.Load(Settings.Default.TamConfigurationPath, _topology, true, true, this);

            // Get its first (and only) axis of the found drive.
            _axis = _drive.Axes[0];

            var axisRegister = (Axis)_drive.Axes[0].Register;

            // Create two acquisition variables for position and position error.
            // Specify 0 for the sampling time, which will be rounded to the lowest possible sampling time.
            ITamReadonlyRegister posReg = axisRegister.Signals.PositionController.ActualPosition;
            _positionVariable = posReg.CreateVariable(TimeSpan.FromSeconds(0));
            ITamReadonlyRegister errorReg = axisRegister.Signals.PositionController.PositionError;
            _positionErrorVariable = errorReg.CreateVariable(TimeSpan.FromSeconds(0));

            // As soon as multiple variables are to be recorded synchronized, create an acquisition object.
            // Otherwise, you may use the Acquire methods of the variable itself.
            _acquisition = TamAcquisition.Create(_positionVariable, _positionErrorVariable);
        }

        /// <exception cref="TamException">Enabling failed.</exception>
        private void EnableDrive() {

            // Prepare for the use of the WaitForTermination method.
            _drive.AddStateObserver(this);

            // Set the drive operational, i.e. switch the power section on.
            _drive.SwitchOn().WaitForTermination();

            // Enable the axis controller.
            _axis.Control(AxisControlCommands.Enable).WaitForTermination();
        }

        /// <exception cref="TamException">Disabling failed.</exception>
        private void DisableDrive() {

            // Disable the axis controller.
            _axis.Control(AxisControlCommands.Disable).WaitForTermination();

            // Switch the power section off.
            _drive.SwitchOff().WaitForTermination();

            // Counter part for AddStateObserver.
            _drive.RemoveStateObserver(this);
        }

        /// <summary>
        /// Plots one data series.
        /// </summary>
        private static void Fill(Series series, ITamVariable<double> variable, double scaling) {
            DataPointCollection points = series.Points;
            points.SuspendUpdates();
            points.Clear();
            foreach (double value in variable) {
                points.AddY(value * scaling);
            }
            points.ResumeUpdates();
        }

        /// <summary>
        /// Called when an acquisition completes.
        /// </summary>
        /// <remarks>Must be called on the main thread.</remarks>
        private void OnAcquired(bool hasMore, AcquisitionException ex) {

            // don't plot anymore if the form is already closed
            if (!Visible) return;

            if (ex == null) {

                // plot
                Fill(_chart.Series["Position"], _positionVariable, 1);
                Fill(_chart.Series["Position Error"], _positionErrorVariable, 1E3);

                // repeat recording
                Acquire();
            } else {
                MessageBox.Show(ex.Message, "Failure during acquisition", MessageBoxButtons.OK,
                    MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
            }
        }

        /// <summary>
        /// Start an acquisition.
        /// </summary>
        /// <remarks>Must be called on the main thread.</remarks>
        private void Acquire() {

            // Start asynchronously in order to not block the main thread.
            // pass a delegate to the method OnAcquired called when acquire completes.
            // The delegate will be called using the synchronization context of this thread.
            _acquisition.AcquireAsync(TimeSpan.FromMilliseconds(_trackBarDuration.Value), _trigger,
                new AcquireFuture(OnAcquired));
        }

        /// <summary>
        /// Recreates the trigger from a new value.
        /// </summary>
        /// <remarks>Must be called on the main thread.</remarks>
        private void RefreshTrigger() {

            // Create a hardware trigger on velocity with raising edge on the level dictated by the trigger level
            // track bar.
            _trigger = new TamTrigger(((Axis)_axis.Register).Signals.PathPlanner.Velocity, PublicationCommand.RaisingEdge,
                (float)_trackBarTriggerLevel.Value);
        }

        /// <summary>
        /// Does some work with a drive.
        /// </summary>
        private void Worker() {
            #region Preparation

            // create topology, boot system, find drive
            Startup();
            if (_drive == null) return;

            EnableDrive();

            // Call the next two methods on the GUI thread as required.
            // The ThreadStart delegate is set to an anonymous method constructed from a lambda expression.
            BeginInvoke(new ThreadStart(() => { RefreshTrigger(); Acquire(); }));

            #endregion Preparation

            #region Work

            // move forth and back
            // stop moving when the form is closed
            while (Visible) {

                // command moves and wait until the moves are completed
                _axis.MoveRelative(1).WaitForTermination();
                _axis.MoveRelative(-1).WaitForTermination();

                // ensure _terminate is fresh
                Thread.MemoryBarrier();
            }
            #endregion Work

            #region Tear down
            DisableDrive();
            _acquisition.Dispose();
            _topology.Dispose();
            #endregion Tear down
        }
        #endregion Acquisition demo code

        #region GUI code
        private void OnTrackBarTriggerLevelScroll(object sender, EventArgs e) => RefreshTrigger();

        /// <summary>
        /// Raises the <see cref="Form.Shown"/> event.
        /// </summary>
        protected override void OnShown(EventArgs e) {

            // Create and start a new thread executing the method Worker, named "Worker".
            // Note the collection initializer used to set a property of the newly constructed thread.
            new Thread(new ThreadStart(Worker)) {
                Name = "Worker"
            }.Start();

            base.OnShown(e);
        }
        #endregion GUI code
    }
}
 

发布了181 篇原创文章 · 获赞 35 · 访问量 76万+

猜你喜欢

转载自blog.csdn.net/dacong/article/details/96729967