ACOP.NET API
Gallery

Bunch Current Display

bunches0.jpg

A bunch current display (a modicum of programming)

This application consists of three AcopCharts, two AcopTables, an AcopWheelSwitch, an AcopLink component, and an AcopChooser, as well as three common check box controls. The top chart is in histogram display mode (adjusted histogram or normal histogram, depending on the 'Anti-Aliasing' check box setting). The charts belows this show the selected bunch profile and a trend of the bunch current for the selected bunch.
The user selects a bunch via the AcopChooser control. The AcopTables (9 rows and 1 column, and 1 row and 4 columns) show various related parameters.

In addition, the AcopStatusBar has been expanded to offer a navigation choice between a primary target server and a secondary target server.

The application offers several nice features which can only be coded. For instance, the running application dynamically determines which bunches are filled and highlights these in the AcopChooser. Also clicking on a particular bunch in the histogram display likewise selects a bunch as if it had been selected by the AcopChooser.

If the application were configured only (i.e. no extra coding), it would still provide a viable bunch current display application. There would however be no ability to select a bunch by simply clicking on the main histogram chart (the chooser would be the only avenue for bunch selection). And the highlighting of the filled bunches would not be available. Navigating to secondary (redundant) hardware would be only partially achievable thru the use of an additional chooser. Information in the bottom (transfer efficiency) table would also be incomplete.

The modicum of code responsible for these features is shown in the snippets below:

The main form is simply named Form1. In its constructor, we get the learned device list from the chooser after InitializeComponent() has completed.

We also declare a few local variables which will be of use in some other method calls.

Furthermore, we have applied event delegates for Form Load and Resize events. In Form1_Load we start some background links to obtain transfer information to display in the bottom AcopTable. We do it like this rather than apply direct links within the table because we want to manipulate the incoming data prior to display in the table.

Normally, we can just make use of anchoring to manage resizing the application. Here we trap the resize event in order to adjust the size of and place the interior chart at our desired location.

public partial class Form1 : Acop.AcopForm
{
public Form1()
{
InitializeComponent();
bunLabels = acopChooser1.GetDeviceList();
}
String[] bunLabels = null;
double[] bunCurrents = null;
Boolean[] bunFilled = null;
int buncurChartDisplayIndex = -1;
private void Form1_Load(object sender, EventArgs e)
{
TransferLink.LinkProperty = "Efficiency.Std";
TransferLink.Attach(xferEff, null, 0, 1000, null);
TransferLink.LinkProperty = "Duration";
TransferLink.Attach(xferDur, null, 0, 1000, null);
TransferLink.LinkDevice = "Petra.EWeg.Ej";
TransferLink.LinkProperty = "NumberOfTransfers";
TransferLink.Attach(xferNum, null, 0, 1000, null);
bunchToolTip.SetToolTip(acopChooser1, "select bunch (dark green: filled bunches)");
bunchToolTip.SetToolTip(profileReferenceCheckbox, "shows bunch-1 as reference (when bunch-1 is not already the selected bunch)");
bunchToolTip.SetToolTip(trendReferenceCheckbox, "shows bunch-1 as reference (when bunch-1 is not already the selected bunch)");
bunchToolTip.SetToolTip(AntiAliasCheck, "avoids overlaying histogram lines");
}
private void Form1_Resize(object sender, EventArgs e)
{ // besides the anchoring, we need to reposition some of the components ...
int h = transferInfoTable.Location.Y - 6; // the bottom table defines the available space
buncurChart.Height = h / 2 - 12;
int x = ProfileChart.Location.X;
ProfileChart.Location = new Point(x, h / 2 + 12);
ProfileChart.Width = TrendChart.Location.X - 18;
ProfileChart.Height = h / 2 - 12;
int y = profileReferenceCheckbox.Location.Y;
int cbw = profileReferenceCheckbox.Width;
profileReferenceCheckbox.Location = new Point(x + ProfileChart.Width - cbw - 7, y);
x = TrendChart.Location.X;
TrendChart.Location = new Point(x, h / 2 + 12);
TrendChart.Width = ProfileChart.Width;
TrendChart.Height = h / 2 - 12;
trendReferenceCheckbox.Location = new Point(x + ProfileChart.Width - cbw - 7, y);
}

The primary (top) chart is named buncurChart. We've made an acopLinkUpdate event delegate and when it fires, we want to make a note of the chart display index so that we can make sure that the selected bunch is tagged (we've set the tag color to DarkGreen). We also retrieve the attached bunch current readback data, so that we can form a boolean array to pass to the AcopChooser's SetDeviceHighlighted() method.

private void buncurChart_acopLinkUpdate(object sender, Acop.Link lnk)
{
if (acopChooser1.InvokeRequired)
{
buncurChart.LinkUpdateInvoker(buncurChart_acopLinkUpdate, sender, lnk);
return;
}
if (buncurChartDisplayIndex < 0)
{ // 1st update ...
buncurChartDisplayIndex = lnk.GetDisplayId();
}
bunCurrents = lnk.GetOutputYAxis();
if (bunFilled == null)
{ // 1st update ...
bunFilled = new Boolean[bunCurrents.Length];
selectedBunchIndex = 0;
buncurChart.IsTagged(0, buncurChartDisplayIndex, true);
}
for (int i=0; i<bunCurrents.Length; i++)
{ // who's got some current ?
bunFilled[i] = bunCurrents[i] > 30;
}
acopChooser1.SetDeviceHighlighted(bunFilled);
}

We make a method selectBunch() which makes sure that the correct bunch is tagged in the primary chart and there are no references when a new bunch is selected.

private int selectedBunchIndex = -1;
private void selectBunch(int idx)
{
if (bunLabels == null) return;
if (idx < 0 || idx >= bunLabels.Length) return;
profileReferenceCheckbox.Checked = false;
ProfileChart.ShowLegend = false;
TrendChart.ShowLegend = false;
trendReferenceCheckbox.Checked = false;
buncurChart.IsTagged(selectedBunchIndex, buncurChartDisplayIndex, false);
buncurChart.IsTagged(idx, buncurChartDisplayIndex, true);
selectedBunchIndex = idx;
TrendChart.FrameCaption = "bunch current trend (" + bunLabels[idx] + ")";
}

If the selected bunch is not the reference bunch then we give the user the option of overlaying the reference bunch on both the profile and the trend charts (and applying a chart legend as well). So we've added check boxes to allow this and make use of the following event delegates.

private int bunReferenceIndex = 0;
private float[] referenceProfile = new float[40];
private void profileReferenceCheckbox_Click(object sender, EventArgs e)
{
if (profileReferenceCheckbox.Checked)
{
if (selectedBunchIndex == bunReferenceIndex)
{
profileReferenceCheckbox.Checked = false;
return;
}
ProfileChart.ForegroundColor = Color.DarkRed;
ProfileChart.PlotMode = Acop.AcopChart.AcopPlotMode.polyline;
ProfileChart.LinkDevice = bunLabels[bunReferenceIndex];
ProfileChart.Attach();
ProfileChart.RemoveLegend(0);
ProfileChart.RemoveLegend(1);
ProfileChart.AddLegendItem(bunLabels[selectedBunchIndex], Color.RoyalBlue, Acop.AcopChart.AcopPlotMode.polyline_dots, 0);
ProfileChart.AddLegendItem(bunLabels[bunReferenceIndex], Color.DarkRed, Acop.AcopChart.AcopPlotMode.polyline, 1);
ProfileChart.ShowLegend = true;
}
else
{
String addr = "/" + TrendChart.LinkContext + "/" + TrendChart.LinkServer + "/" + bunLabels[bunReferenceIndex];
ProfileChart.Detach(addr,ProfileChart.LinkProperty);
ProfileChart.ShowLegend = false;
}
}
private void trendReferenceCheckbox_Click(object sender, EventArgs e)
{
if (trendReferenceCheckbox.Checked)
{
if (selectedBunchIndex == bunReferenceIndex)
{
trendReferenceCheckbox.Checked = false;
return;
}
TrendChart.LinkDevice = bunLabels[bunReferenceIndex];
TrendChart.ForegroundColor = Color.DarkRed;
TrendChart.Attach();
TrendChart.RemoveLegend(0);
TrendChart.RemoveLegend(1);
TrendChart.AddLegendItem(bunLabels[selectedBunchIndex], Color.RoyalBlue, Acop.AcopChart.AcopPlotMode.polyline, 0);
TrendChart.AddLegendItem(bunLabels[bunReferenceIndex], Color.DarkRed, Acop.AcopChart.AcopPlotMode.polyline, 1);
TrendChart.ShowLegend = true;
}
else
{
String addr = "/" + TrendChart.LinkContext + "/" + TrendChart.LinkServer + "/" + bunLabels[bunReferenceIndex];
TrendChart.Detach(addr,TrendChart.LinkProperty); // in case auto-reattached to initial bunch-1 selection
TrendChart.ShowLegend = false;
}
}

When our background links to the transfer information fire an update event we do our manipulation and place the desired information in the bottom AcopTable. Note that we need to make use of the InvokeRequired paradigm as we're updating GUI methods from another component (namely an AcopTable).

DateTime utcOff = new DateTime(1970, 1, 1);
float[] xferEff = new float[1];
float[] xferDur = new float[1];
Int32[] xferNum = new Int32[1];
private void TransferLink_acopLinkUpdate(object sender, Acop.Link lnk)
{
if (transferInfoTable.InvokeRequired)
{
transferInfoTable.LinkUpdateInvoker(TransferLink_acopLinkUpdate, sender, lnk);
return;
}
double dt = lnk.GetTimeStamp();
float xeff = (float)(xferEff[0] * 100.0);
DateTime t = new DateTime((long)(dt * 1.0e7)+ utcOff.Ticks, DateTimeKind.Utc);
transferInfoTable.SetTableValue(0, 0, "time : " + t.ToLocalTime().ToString("dd-MMM-yy hh:mm:ss"));
transferInfoTable.SetTableValue(0, 1, "efficiency : " + xeff.ToString("F3") + " %");
transferInfoTable.SetTableValue(0, 2, "duration : " + xferDur[0].ToString("F3") + " sec");
transferInfoTable.SetTableValue(0, 3, "number transfers : " + xferNum[0].ToString());
}

We've added an event delegate for the primary (top) acop chart mouse click event. We want to let the user select a target bunch by simply clicking on one of the histogram lines.

private void buncurChart_acopClicked(object sender, Acop.AcopChartUtil.AcopEvent e)
{
int idx = e.GetArrayIndex();
if (bunLabels == null) return;
if (bunCurrents == null) return;
if (idx < 0) return;
if (idx >= bunLabels.Length) return;
if (idx >= bunCurrents.Length) return;
acopChooser1.LinkDevice = bunLabels[idx];
}

Navigating to an alternate redundant set of servers (e.g. for checking readback integrity) involves a bit more that simply switching one server to another. So rather than use an AcopChooser we've added an extra menu item to the status bar and we trap the selection events and apply the code below when the user wants to target the same information from a redundant set of servers.

int navigateIndex = 0; // 0 = primary, 1 = secondary servers ...
String[] bunchServers = new String[] { "BunchCurrents", "BunchCurrents.Spare" };
String[] tableList = new String[] {
"/PETRA/BunchProfile/Bunch-1/SummedCurrent;/PETRA/BunchProfile/Bunch-1/MaximumCurrent;... <<text omitted",
"/PETRA/BunchCurrents.Spare/Bunch-1/SummedCurrent;/PETRA/BunchCurrents.Spare/Bunch-1/M... <<text omitted"
};
String[] captionText = new String[] {
"Bunch Currents (PETRA - Primary Bunch Server)",
"Bunch Currents (PETRA - Secondary Bunch Server)"
};
private void navigateToTarget(int targetIndex)
{
if (navigateIndex == targetIndex) return;
if (targetIndex < 0 || targetIndex > 1) return;
navigateIndex = targetIndex;
String dev = bunLabels == null ? "#0" : bunLabels[0];
String addr = "/" + ProfileChart.LinkContext + "/" + bunchServers[navigateIndex] + "/" + dev;
buncurChart.IsTagged(selectedBunchIndex, buncurChartDisplayIndex, false);
selectedBunchIndex = -1;
buncurChart.Reassign(addr, buncurChart.LinkProperty);
ProfileChart.Reassign(addr, ProfileChart.LinkProperty);
TrendChart.Reassign(addr, TrendChart.LinkProperty);
primaryServerToolStripMenuItem.Checked = navigateIndex == 0;
secondaryServerToolStripMenuItem.Checked = navigateIndex == 1;
profileReferenceCheckbox.Checked = false;
ProfileChart.ShowLegend = false;
TrendChart.ShowLegend = false;
trendReferenceCheckbox.Checked = false;
// and the table values ...
acopTable1.ReassignList(tableList[navigateIndex]);
// form caption:
this.Text = captionText[navigateIndex];
}
private void NavigateToPrimary(object sender, EventArgs e)
{
navigateToTarget(0);
}
private void NavigateToSecondary(object sender, EventArgs e)
{
navigateToTarget(1);
}

Finally, we apply simple code in the event delegates for auto-scaling the primary chart, changing the anti-aliasing setting, and some additional logic when the AcopChooser is used to select a new target bunch.

private void buncurChart_AcopAutoScaled(object sender, Acop.AcopChartUtil.AcopAutoScaleEvent e)
{
acopWheelSwitch1.WheelSwitchValue = buncurChart.YMax;
}
private void AntiAliasCheck_CheckedChanged(object sender, EventArgs e)
{
Acop.AcopChart.AcopPlotMode m = AntiAliasCheck.Checked ?
Acop.AcopChart.AcopPlotMode.histogram_rastering :
buncurChart.SetPlotMode(0, m);
}
private void acopChooser1_acopAddressUpdate(object sender, Acop.AcopChooser chooser)
{
selectBunch(acopChooser1.GetDeviceIndex());
}
}

Inductive Currents Display

InductiveCurrents0.jpg

Inductive Current Traces (almost no programming)

This application consists primarily of a single AcopChart target waveform (e.g. oscilloscope trace) displays, making use of the TraceSettings properties at design time. An AcopLabel is used to display the ambient beam current. Otherwise, there are various common controls (buttons and checkboxes) used to navigate and apply settings.

Overview Panel Display

LinacOverview0.jpg

Linac Overview Panel (almost no programming)

This application offers a synopic display of the modulators and other components in the PETRA linac. It consists of various Acop Shape components as well as a number of Acop Table columns. The magnet and modulator shapes offer control panels in their context menus.

A small amount of programming was used to set the background colors of the modulators and some of the table cells from among a set of four possible colors obtained from other sources than that tied to the GUI components themselves.

Motor Steering Display

motors0.jpg

A motor steering application (very little programming)

The application allows some motor steering for three distinct motors, making use of an AcopChart, an AcopBitRegister, an AcopWheelSwitch, and several AcopButtons for each displayed motor.

Stepper Motor Control Display

MotorControl1.jpg

A stepper motor steering application (very little programming)

The application allows stepper motor steering and scanning the selected motor. It uses uses an AcopChart, an AcopBitRegister, an AcopLabel, two AcopWheelSwitches, an AcopSlider, an AcopJaw shape component, several AcopButtons, and an AcopChooser two select the target motor. A normal button component is used to launch a Scan Panel

MotorControl2.jpg

A stepper motor scan panel(very little programming)

The scan panel itself uses an AcopChart, an AcopChooser, an AcopLabel, an AcopWheelSwitch, and an AcopLink component as well as the common components: ListView, two buttons, and a label, along with a timer and a saveDialog component.
Here the AcopWheelSwitch is used as a simple way to set the number of scan steps.

In this case, the main form has been given the name MainForm.
In this case we would like to have a general motor steering application which is capable of controlling any stepper motor of this particular class. So we want to accept a target address in the command line, along with a scan target address, and assign this address to all relevant components.

So the strategy of assigning an address at design time and setting AutoLinkAttach to true cannot be used. We need to make a note of which Acop controls are read-only and which are writeable so that we can adjust the AcopChooser targets accordingly. And we need to delay the Acop monitoring until the target addresses have been assigned. So we parse the command line in the MainForm constructor and introduce a method to activateLinks.

namespace MotorSteering
{
public partial class MainForm : Acop.AcopForm
{
Boolean initDone = false;
String target = null;
String scantarget = null;
Acop.AcopUserComponent[] rbctrls = null;
Acop.AcopUserComponent[] woctrls = null;
public MainForm()
{
InitializeComponent();
instance = this;
target = acopChart1.LinkAddress;
// allow the command line to target another motor server !
// => need to keep track of readback components and write-only components
rbctrls = new Acop.AcopUserComponent[]
{ // those controls which have a readback value
acopChart1, acopLabel1, acopSlider1, acopBitRegister1,
acopWheelSwitch1, acopWheelSwitch2, acopJaw1
};
woctrls = new Acop.AcopUserComponent[]
{ // those controls with no readback value (write-only)
acopButton1, acopButton2, acopButton3, acopButton4
};
parseCmdLine();
this.Text = "Motor Control Panel for /" + acopChart1.LinkContext + "/" + acopChart1.LinkServer;
acopChooser1.LinkAddress = target;
// now start the links and displays ...
activateLinks();
initDone = true;
}
void parseCmdLine()
{
String[] args = Environment.GetCommandLineArgs();
if (args.Length < 2) return;
int idx = -1;
foreach (String arg in args)
{
if (!arg.StartsWith("-")) continue;
if (arg.StartsWith("-target",StringComparison.CurrentCultureIgnoreCase))
{
idx = arg.IndexOf('=');
if (idx > 0) setTargetAddress(arg.Substring(idx + 1));
continue;
}
if (arg.StartsWith("-scan", StringComparison.CurrentCultureIgnoreCase))
{
idx = arg.IndexOf('=');
if (idx > 0) setScanTargetAddress(arg.Substring(idx + 1));
continue;
}
}
}
void setScanTargetAddress(String addr)
{
scantarget = addr;
}
public String GetScanTarget()
{
return scantarget;
}
void setTargetAddress(String addr)
{
target = addr;
foreach (Acop.AcopUserComponent a in rbctrls) a.LinkAddress = addr;
foreach (Acop.AcopUserComponent a in woctrls) a.LinkAddress = addr;
acopChooser1.LinkContext = acopChart1.LinkContext;
acopChooser1.LinkServer = acopChart1.LinkServer;
acopChooser1.LinkProperty = acopChart1.LinkProperty;
acopChooser1.LinkDevice = acopChart1.LinkDevice;
}
public String GetTarget()
{
return target;
}
void activateLinks()
{
foreach (Acop.AcopUserComponent a in rbctrls) a.Attach();
}

As we want to offer a Scan Panel, which will be an additional Form, we will keep track of our MainForm instance along with some status parameters: isStepping and isMoving. An ordinardy button component is used to launch the Scan Panel.

// keep track of a few variables for the scan form ...
static MainForm instance = null;
public static MainForm GetInstance()
{
return instance;
}
public Boolean isStepping = false;
public Boolean isMoving = false;
int motSts = 0;
private void acopBitRegister1_acopLinkUpdate(object sender, Acop.Link lnk)
{
if (lnk.GetStatus() != 0) return;
motSts = lnk.GetOutputDataAsInteger();
isStepping = ((motSts & 0x04) != 0);
isMoving = ((motSts & 0x01) != 0);
}
public int posSts = 0;
public double motPos = 0.0;
public Boolean motPosIdle = true;
private void acopLabel1_acopLinkUpdate(object sender, Acop.Link lnk)
{
posSts = lnk.GetStatus();
if (posSts != 0) return;
motPos = lnk.GetOutputDataAsNumber();
if (!isStepping && !isMoving) motPosIdle = true;
}
private void acopButton3_acopApply(object sender, Acop.Link lnk)
{
isStepping = true;
}
ScanForm scanFrm = null;
private void button1_Click(object sender, EventArgs e)
{
if (scanFrm == null || scanFrm.IsDisposed)
{ // show the scan form if not already displayed ...
scanFrm = new ScanForm();
scanFrm.Show();
if (this.Location.Y < Screen.PrimaryScreen.Bounds.Height / 2)
{ // try to position it directly under the top form ...
scanFrm.Location = new Point(this.Location.X, this.Location.Y + this.Height);
scanFrm.Width = this.Width;
}
}
}
int ssSts = 0;
public double stepSize = 0.5;
private void acopWheelSwitch2_acopLinkUpdate(object sender, Acop.Link lnk)
{
ssSts = lnk.GetStatus();
if (ssSts != 0) return;
stepSize = lnk.GetOutputDataAsNumber();
}
// if a reset comes in, we have to handle it ourselves because
// we're allowing a different target server from the command line ...
private void acopStatusBar1_acopResetLinks(object sender, EventArgs e)
{
activateLinks();
}
// rather than supply a long list of components in the designer,
// just make use of the routines we made for setting a new target ...
private void acopChooser1_acopAddressUpdate(object sender, Acop.AcopChooser chooser)
{
if (!initDone) return;
setTargetAddress(chooser.LinkAddress);
activateLinks();
}
}

The Scan Panel uses an AcopLink component to send the Step commands to the motor server and a Timer component to determine if a step has completed and then either to continue on to the next step or stop the scan if the number of scan steps (settable via an AcopWheelSwitch) has been reached.

Hence, much of the logic is in the Timer's Tick delegate. In particular an AcopChart is used to display the latest scan results (and is not itself attached to any link address).

public partial class ScanForm : Acop.AcopForm
{
public ScanForm()
{
InitializeComponent();
top = MainForm.GetInstance();
target = top.GetTarget();
if (target != null) acopLink1.LinkAddress = target;
parseScanTarget();
listView1.View = View.Details;
listView1.Columns.Add("Position (mm)", 80);
listView1.Columns.Add("Scan Value ", 120);
}
void parseScanTarget()
{
String s = top.GetScanTarget();
if (s == null || s.Length == 0) return;
String[] parts = s.Split(new char[] { '/', '[', ']' },StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 4) return;
String dev = "";
for (int i = 2; i < parts.Length - 1; i++)
{ // watch out for embedded '/' in the device names ...
dev += parts[i];
if (i < parts.Length - 2) dev += "/";
}
acopChooser1.LinkContext = parts[0];
acopChooser1.LinkServer = parts[1];
acopChooser1.LinkDevice = dev;
acopChooser1.LinkProperty = parts[parts.Length - 1];
applyChooserSelection = true; // must wait until form is shown
}
MainForm top = null;
int nSteps = 10;
int stepNumber = 0;
double[] y = new double[1000];
double[] x = new double[1000];
int dpdSts = 0;
double dpdVar = 0.0;
Boolean dpdVarIdle = true;
String target = null;
Boolean applyChooserSelection = false;
private void ScanBtn_Click(object sender, EventArgs e)
{
top.isMoving = true;
stepNumber = 0;
dpdVarIdle = top.motPosIdle = false;
timer1.Enabled = true;
acopLink1.LinkProperty = "Move.START";
int cc = acopLink1.Execute(null, 0.0, "WRITE", 1000);
if (cc != 0)
{
System.Diagnostics.Debug.WriteLine("!" + cc);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
int cc = 0;
if (top.isStepping || top.isMoving) return;
if (dpdSts != 0 || top.posSts != 0) return;
if (!top.motPosIdle || !dpdVarIdle) return;
if (stepNumber == 0)
{
top.isStepping = true;
acopLink1.LinkProperty = "StepMove.START";
cc = acopLink1.Execute(null, null, "WRITE", 200);
acopChart1.ClearScreen();
acopLabel2.GetLink().GetDisplayYMax();
acopChart1.XMin = 0;
acopChart1.XMax = top.stepSize * nSteps * 1.1; // 10 % larger
acopChart1.YMin = acopLabel2.GetLink().GetDisplayYMin() * 0.9;
acopChart1.YMax = acopLabel2.GetLink().GetDisplayYMax() * 1.1;
acopChart1.FrameCaption = acopLabel2.LinkProperty + " vs. motor position";
acopChart1.XAxisLabel = "Position (mm)";
listView1.Items.Clear();
String colstr = acopLabel2.LinkProperty;
String units = acopLabel2.GetLink().GetDisplayYUnits();
if (units.Length > 0 && !units.Equals("none",StringComparison.InvariantCultureIgnoreCase))
colstr = colstr +" (" + acopLabel2.GetLink().GetDisplayYUnits() + ")";
listView1.Columns[1].Text = colstr;
stepNumber++;
return;
}
x[stepNumber-1] = top.motPos;
y[stepNumber-1] = dpdVar;
acopChart1.ClearScreen();
acopChart1.Draw(y, x, stepNumber, nSteps);
listView1.Items.Add(new ListViewItem(new string[] { x[stepNumber - 1].ToString("F4"), y[stepNumber - 1].ToString("F4") }));
if (stepNumber++ < nSteps)
{
top.isStepping = true;
cc = acopLink1.Execute(null, null, "WRITE", 200);
return;
}
timer1.Enabled = false;
}
private void acopLabel2_acopLinkUpdate(object sender, Acop.Link lnk)
{
dpdSts = lnk.GetStatus();
if (dpdSts != 0) return;
dpdVar = lnk.GetOutputDataAsNumber();
if (!top.isStepping && !top.isMoving) dpdVarIdle = true;
}
private void button1_Click(object sender, EventArgs e)
{
saveFileDialog1.ShowDialog();
}
private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
{
StreamWriter sw = new StreamWriter(saveFileDialog1.FileName);
sw.WriteLine(listView1.Columns[0].Text + ", " + listView1.Columns[1].Text);
for (int i=0; i<nSteps; i++)
{
sw.WriteLine(x[i].ToString("F4")+", "+y[i].ToString("F4"));
}
sw.Close();
}
private void acopWheelSwitch1_acopApply(object sender, Acop.Link lnk)
{
nSteps = (int)acopWheelSwitch1.WheelSwitchValue;
}
private void ScanForm_Shown(object sender, EventArgs e)
{
if (applyChooserSelection)
acopChooser1.ApplySelection(sender);
}
}

Datenschutzerklaerung   -   Data Privacy Policy
Generated for ACOP.NET API by  doxygen 1.5.8