Network.Adapter, Network.Camera.CLP1611, Network.Camera.RoI Network.Tftp and Network.Telnet components added. Camera detection and live strean work OK.

This commit is contained in:
Milan Hanajik 2016-12-29 12:30:31 +01:00
parent ad51719d92
commit 254afc3978
32 changed files with 4686 additions and 6 deletions

View File

@ -271,6 +271,7 @@ namespace DeviceTest
operation4 = null;
workerThreadRunning = false;
string initializedDeviceName = "none";
try
{
if (parentFactory != null && parentCfg != null)
@ -291,8 +292,8 @@ namespace DeviceTest
foreach (var d in tbfDevices)
{
initializedDeviceName = d.Name;
d.Initialize();
Debug.WriteLine("{0}.Initialize()", d.Name);
}
SaveSettings(); /// Save settings after successful initialization
@ -313,8 +314,10 @@ namespace DeviceTest
workerThreadRunning = false;
workerThread = null;
MessageBox.Show(string.Format("Exception: {0}", exc.Message), "Initialization failed",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
MessageBox.Show(exc.Message,
string.Format("'{0}' initialization failed", initializedDeviceName),
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
startButton.Enabled = true;
stopButton.Enabled = false;
@ -334,6 +337,10 @@ namespace DeviceTest
{
operation1 = (tbfComponent4Op as TBF.BenchControl.Modbus.PressureMeter.Meret.PressureMeter).ReadPressureOp(ref floatBox);
}
else if (tbfComponent4Op is TBF.BenchControl.Network.Camera.CLP1611.Camera)
{
operation1 = (tbfComponent4Op as TBF.BenchControl.Network.Camera.CLP1611.Camera).LiveStreamOp();
}
while (workerThreadRunning)
{
@ -341,7 +348,7 @@ namespace DeviceTest
if (workerThreadRunning)
{
foreach (var d in tbfDevices) { d.RunDeviceBefore(); Debug.WriteLine("{0}.RunDeviceBefore()", d.Name); }
foreach (var d in tbfDevices) d.RunDeviceBefore();
if (tbfComponent4Op != null)
{
@ -380,11 +387,11 @@ namespace DeviceTest
}
}
foreach (var d in tbfDevices) { d.RunDeviceAfter(); Debug.WriteLine("{0}.RunDeviceAfter()", d.Name); }
foreach (var d in tbfDevices) d.RunDeviceAfter();
}
}
foreach (var d in tbfDevices) { d.StopDevice(); Debug.WriteLine("{0}.StopDevice()", d.Name); }
foreach (var d in tbfDevices) d.StopDevice();
}
private void stopButton_Click(object sender, EventArgs e)

View File

@ -0,0 +1,11 @@
using System.Net;
namespace TBF.BenchControl.GenericDevices
{
public interface INetworkAdapter : TBF.BenchControl.Generic.IComponent
{
IPAddress IPAddress { get; }
IPAddress NetMask { get; }
IPAddress BroadcastAddress { get; }
}
}

View File

@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Web;
namespace TBF.BenchControl.Network.Adapter
{
/// <summary>
/// Class for the storing information about a network adapter.
/// </summary>
public class AdapterInfo
{
public string Description; /// Description of the network adapter.
public IPAddress IPAddress; /// IP address of the network adapter.
public IPAddress NetMask; /// Mask of the network adapter.
/// Constructor
public AdapterInfo(string description)
{
Description = description;
}
public IPAddress GetBroadcastAddress()
{
byte[] ipAdressBytes = IPAddress.GetAddressBytes();
byte[] subnetMaskBytes = NetMask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
///------------------------------------------------------
/// Static members and functions
///------------------------------------------------------
/// <summary>
/// Private list of the available network adapters.
/// </summary>
static List<AdapterInfo> netAdapters;
/// <summary>
/// Retrieves the list of network adapters.
/// Never returns 'null', calls RefreshNetAdaptersInfo() the first time it is used.
/// If you want to refresh the list of the network adapters later, explicitly call
/// the RefreshNetAdaptersInfo() method.
/// </summary>
public static List<AdapterInfo> NetAdapters
{
get
{
if (netAdapters == null) RefreshNetAdaptersInfo();
return netAdapters;
}
}
/// <summary>
/// Refreshes the information about network adapters stored in the netAdapters list.
/// </summary>
public static void RefreshNetAdaptersInfo()
{
if (netAdapters == null) netAdapters = new List<AdapterInfo>();
else netAdapters.Clear();
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in nics)
{
AdapterInfo info = new AdapterInfo(adapter.Description);
netAdapters.Add(info);
IPInterfaceProperties properties = adapter.GetIPProperties();
if (properties == null)
continue;
UnicastIPAddressInformationCollection uniCast = properties.UnicastAddresses;
if (uniCast == null)
continue;
foreach (UnicastIPAddressInformation uni in uniCast)
{
if (info == null)
{
info = new AdapterInfo(adapter.Description);
netAdapters.Add(info);
}
info.IPAddress = uni.Address;
if (uni.IPv4Mask != null) info.NetMask = uni.IPv4Mask;
info = null;
}
}
}
/// <summary>
/// Gets the loopback network adapter.
/// </summary>
public static AdapterInfo LoopbackAdapter
{
get
{
List<AdapterInfo> adapters = NetAdapters;
foreach (AdapterInfo a in adapters)
{
if (a.IPAddress != null && a.IPAddress.Equals(IPAddress.Loopback)) return a;
}
return null;
}
}
/// <summary>
/// Gets the default network adapter - the first one which his not a loopback adapter.
/// Network adapters with IP address have preference, adapters without IP address
/// (like an unconnected wireless adapter) are used just when there is no adapter with IP.
///
/// Default adapter is used when none was specified in the configuration file
/// and it is used to initialize Options-General dialog-tab.
/// </summary>
public static AdapterInfo DefaultAdapter
{
get
{
List<AdapterInfo> adapters = NetAdapters; /// Make a copy to avoid interference
AdapterInfo adapterWithoutIP = null; /// Use this one if there is no other better adapter
foreach (AdapterInfo a in adapters)
{
if (!a.IPAddress.Equals(IPAddress.Loopback))
{
if (a.IPAddress != null) return a;
else if (adapterWithoutIP == null) adapterWithoutIP = a;
}
}
return adapterWithoutIP == null ? LoopbackAdapter : adapterWithoutIP;
}
}
/// <summary>
/// Get network adapter info from the description string.
/// </summary>
/// <param name="description">Description</param>
/// <returns>AdapterInfo object reference</returns>
public static AdapterInfo GetNetAdapter(string description)
{
List<AdapterInfo> adapters = NetAdapters;
foreach (AdapterInfo a in adapters)
{
if (a.Description != description ||
a.IPAddress == null ||
a.IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6 ||
a.IPAddress.Equals(IPAddress.Loopback) ||
a.NetMask == null)
{
continue;
}
return a;
}
return DefaultAdapter;
}
/// <summary>
/// Encodes the given string as URL parameter.
/// </summary>
/// <param name="pathToEncode">String to be encoded.</param>
/// <returns>String encoded as the URL parameter.</returns>
public static string UrlPathEncode(string pathToEncode)
{
StringBuilder sb = new StringBuilder(pathToEncode.Length * 3);
sb.Append(pathToEncode);
// % must be first!!!
sb.Replace("%", "%" + Convert.ToInt32('%').ToString("X2"));
// ?, & and / are not convertable by HttpUtility.UrlPathEncode
sb.Replace("?", "%" + Convert.ToInt32('?').ToString("X2"));
sb.Replace("&", "%" + Convert.ToInt32('&').ToString("X2"));
sb.Replace("/", "%" + Convert.ToInt32('/').ToString("X2"));
// Additional chars. E.g.: # didn't work in the path passed to the Uri class. "c:\\sour#ce\\my # proj".
sb.Replace("#", "%" + Convert.ToInt32('#').ToString("X2"));
// Backslash must be removed because it is not accepted by Uri class when passing path into it. E.g.: "C:%5Csource" is not acceptable.
//sb.Replace("\\", "%" + Convert.ToInt32('\\').ToString("X2"));
//// I removed following chars because I do not know if it is good idea to replace them (see previous line with '\\' char).
sb.Replace("\r", "%" + Convert.ToInt32('\r').ToString("X2"));
sb.Replace("\n", "%" + Convert.ToInt32('\n').ToString("X2"));
sb.Replace("\t", "%" + Convert.ToInt32('\t').ToString("X2"));
string s = sb.ToString();
s = HttpUtility.UrlPathEncode(s);
//sb.Length = 0;
//sb.Append(HttpUtility.UrlPathEncode(s));
//for (int i = 0; i < sb.Length; i += 3)
//{
// char c = sb[i];
// if (c == '%')
// continue;
// sb[i] = '%';
// sb.Insert(i + 1, Convert.ToUInt64(c).ToString("X2"));
//}
return s;
}
}
}

View File

@ -0,0 +1,26 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Adapter
{
public class Factory : IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
public void ResetStaticProperties() { Netadapter.ResetStaticProperties(); }
public IComponent DummyComponent() { return new Netadapter(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new Netadapter(cfg); }
public IComponentCfg DefaultConfig() { return new NetadapterCfg(this.GetType().Namespace.Substring(17), this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(typeof(NetadapterCfg), component, this);
}
}
}

View File

@ -0,0 +1,73 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System.Net;
using log4net;
namespace TBF.BenchControl.Network.Adapter
{
public class Netadapter : ComponentBase, TBF.BenchControl.Generic.IDevice, GenericDevices.INetworkAdapter
{
private static readonly ILog log = LogManager.GetLogger(typeof(Netadapter));
public override string ToString()
{
return string.Format("Component={0} IPAddress={1} NetMask={2} Description={3}", Cfg.Name, IPAddress, NetMask, (Cfg as NetadapterCfg).Description);
}
readonly NetadapterCfg netadapterCfg;
readonly IPAddress ipAddress;
readonly IPAddress netMask;
readonly IPAddress broadcastAddress;
public IPAddress IPAddress { get { return ipAddress; } }
public IPAddress NetMask { get { return netMask; } }
public IPAddress BroadcastAddress { get { return broadcastAddress; } }
Tftp.TftpServer tftpServer;
public Netadapter()
{
}
public Netadapter(Generic.IComponentCfg cfg)
: base(cfg)
{
netadapterCfg = cfg as NetadapterCfg;
AdapterInfo.RefreshNetAdaptersInfo();
AdapterInfo netadapter = AdapterInfo.GetNetAdapter(netadapterCfg.Description);
ipAddress = netadapter.IPAddress;
netMask = netadapter.NetMask;
broadcastAddress = netadapter.GetBroadcastAddress();
log.Debug(this.ToString());
}
public void Initialize()
{
if (netadapterCfg.DebugLevel != Config.Entities.DebugMode.Simulate)
{
if (netadapterCfg.TftpServerEnabled)
{
tftpServer = new Tftp.TftpServer();
tftpServer.Start(netadapterCfg.TftpServerDirectory, ipAddress);
}
else
{
tftpServer = null;
}
}
}
public void StopDevice()
{
if (netadapterCfg.DebugLevel != Config.Entities.DebugMode.Simulate)
{
if (tftpServer != null) tftpServer.Stop();
}
}
public void RunDeviceBefore() {}
public void RunDeviceAfter() {}
}
}

View File

@ -0,0 +1,34 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Adapter
{
public class NetadapterCfg : ComponentCfgBase, Generic.IComponentCfg
{
public IComponentCfgCtrl GetControl() { return new NetadapterCfgCtrl(); }
public string Description; /// Description string of the selected network adapter
public bool TftpServerEnabled;
public string TftpServerDirectory;
/// Private parameterless constructor invoked by all other (public) constructors
NetadapterCfg() {}
public NetadapterCfg(string name, IComponentFactory factory)
: this()
{
Name = name;
Factory = factory;
ParentName = string.Empty;
}
public string ToString(int i)
{
return string.Format("Name={0}, Description={1}", Name, Description);
}
}
}

View File

@ -0,0 +1,155 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.Net;
using System.Windows.Forms;
using log4net;
using TBF.BenchControl.Generic;
using System.IO;
namespace TBF.BenchControl.Network.Adapter
{
public partial class NetadapterCfgCtrl : UserControl, IComponentCfgCtrl
{
static readonly ILog log = LogManager.GetLogger(typeof(NetadapterCfgCtrl));
public bool ShowMore { get { return false; } }
NetadapterCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as NetadapterCfg;
Redraw();
}
}
public NetadapterCfgCtrl()
{
InitializeComponent();
}
private void EntryFormCfgCtrl_Load(object sender, EventArgs e)
{
if (ParentForm == null) return; /// Return is executed when tab page is open in Designer
if (config == null) return; /// Control was not loaded, settings were not changed
Localize();
Redraw();
}
void Redraw()
{
classNameLabel.Text = config.Factory.ClassName;
nameTextBox.Text = config.Name;
tftpEnabledCheckBox.Checked = config.TftpServerEnabled;
tftpDirectoryTextBox.Text = config.TftpServerDirectory;
AdapterInfo.RefreshNetAdaptersInfo();
IList<AdapterInfo> adapters = AdapterInfo.NetAdapters;
string cfgAdapter = string.Empty;
foreach (AdapterInfo ai in adapters)
{
string record;
if (ai.IPAddress == null)
{
/// This happens with network adapters that currently have no IP address,
/// for instance not connected wireless or dial-up adapters
record = "???.???.???.???";
}
else
{
/// Do not consider IPv6 adapters as well as "Any", "Broadcast", "Loopback" or "None" addresses
if ((ai.IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) ||
ai.IPAddress.Equals(IPAddress.Any) ||
ai.IPAddress.Equals(IPAddress.Broadcast) ||
ai.IPAddress.Equals(IPAddress.IPv6Any) ||
ai.IPAddress.Equals(IPAddress.IPv6Loopback) ||
ai.IPAddress.Equals(IPAddress.IPv6None) ||
ai.IPAddress.Equals(IPAddress.Loopback) ||
ai.IPAddress.Equals(IPAddress.None))
{
continue;
}
record = ai.IPAddress.ToString();
}
record += " - " + ai.Description;
int i = adapterComboBox.Items.Add(record);
if (ai.Description == config.Description)
{
/// Select the adapter currently in the configuration
adapterComboBox.SelectedIndex = i;
}
}
/// Select the first one if the adapter from the configuration does not exist on the system
if (adapterComboBox.SelectedIndex < 0 && adapterComboBox.Items.Count > 0)
{
adapterComboBox.SelectedIndex = 0;
}
}
void Localize()
{
}
public void Closing()
{
}
public void Unlock()
{
nameTextBox.Enabled = true;
adapterComboBox.Enabled = true;
tftpEnabledCheckBox.Enabled = true;
tftpDirectoryTextBox.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
if (!adapterComboBox.Items.Contains(adapterComboBox.Text))
{
flags = CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Parent Name'";
}
if (tftpEnabledCheckBox.Checked && !Directory.Exists(tftpDirectoryTextBox.Text))
{
flags = CfgUpdateFlags.Error;
message += Environment.NewLine + "TFTP Directory does not exist";
}
return flags;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
/// Network adapter
string strAdapter = adapterComboBox.Text;
int iDash = strAdapter.IndexOf(" - ");
config.Description = iDash < 0 ? "" : strAdapter.Substring(iDash + 3);
config.TftpServerEnabled = tftpEnabledCheckBox.Checked;
config.TftpServerDirectory = tftpDirectoryTextBox.Text;
return flags;
}
}
}

View File

@ -0,0 +1,145 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
namespace TBF.BenchControl.Network.Adapter
{
partial class NetadapterCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.adapterLabel = new System.Windows.Forms.Label();
this.adapterComboBox = new System.Windows.Forms.ComboBox();
this.tftpEnabledCheckBox = new System.Windows.Forms.CheckBox();
this.tftpDirectoryLabel = new System.Windows.Forms.Label();
this.tftpDirectoryTextBox = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(101, 50);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(114, 20);
this.nameTextBox.TabIndex = 5;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(9, 53);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 4;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(98, 24);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 3;
this.classNameLabel.Text = "ComonentName";
//
// adapterLabel
//
this.adapterLabel.AutoSize = true;
this.adapterLabel.Location = new System.Drawing.Point(9, 80);
this.adapterLabel.Name = "adapterLabel";
this.adapterLabel.Size = new System.Drawing.Size(44, 13);
this.adapterLabel.TabIndex = 6;
this.adapterLabel.Text = "Adapter";
//
// adapterComboBox
//
this.adapterComboBox.Location = new System.Drawing.Point(101, 77);
this.adapterComboBox.Name = "adapterComboBox";
this.adapterComboBox.Size = new System.Drawing.Size(333, 21);
this.adapterComboBox.TabIndex = 7;
//
// tftpEnabledCheckBox
//
this.tftpEnabledCheckBox.AutoSize = true;
this.tftpEnabledCheckBox.Enabled = false;
this.tftpEnabledCheckBox.Location = new System.Drawing.Point(101, 115);
this.tftpEnabledCheckBox.Name = "tftpEnabledCheckBox";
this.tftpEnabledCheckBox.Size = new System.Drawing.Size(121, 17);
this.tftpEnabledCheckBox.TabIndex = 12;
this.tftpEnabledCheckBox.Text = "Enable TFTP server";
this.tftpEnabledCheckBox.UseVisualStyleBackColor = true;
//
// tftpDirectoryLabel
//
this.tftpDirectoryLabel.AutoSize = true;
this.tftpDirectoryLabel.Location = new System.Drawing.Point(9, 144);
this.tftpDirectoryLabel.Name = "tftpDirectoryLabel";
this.tftpDirectoryLabel.Size = new System.Drawing.Size(79, 13);
this.tftpDirectoryLabel.TabIndex = 13;
this.tftpDirectoryLabel.Text = "TFTP Directory";
//
// tftpDirectoryTextBox
//
this.tftpDirectoryTextBox.Enabled = false;
this.tftpDirectoryTextBox.Location = new System.Drawing.Point(101, 141);
this.tftpDirectoryTextBox.Name = "tftpDirectoryTextBox";
this.tftpDirectoryTextBox.Size = new System.Drawing.Size(333, 20);
this.tftpDirectoryTextBox.TabIndex = 14;
//
// NetadapterCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tftpDirectoryTextBox);
this.Controls.Add(this.tftpDirectoryLabel);
this.Controls.Add(this.tftpEnabledCheckBox);
this.Controls.Add(this.adapterComboBox);
this.Controls.Add(this.adapterLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "NetadapterCfgCtrl";
this.Size = new System.Drawing.Size(450, 300);
this.Load += new System.EventHandler(this.EntryFormCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.Label adapterLabel;
private System.Windows.Forms.ComboBox adapterComboBox;
private System.Windows.Forms.CheckBox tftpEnabledCheckBox;
private System.Windows.Forms.Label tftpDirectoryLabel;
private System.Windows.Forms.TextBox tftpDirectoryTextBox;
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,220 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Timers;
using log4net;
using Config.Entities;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public class Camera : ComponentBase, Generic.IDevice
{
private static readonly ILog log = LogManager.GetLogger(typeof(Camera));
public override string ToString()
{
return string.Format("{0}, Name={1}, HWAddress={2}, IPAddress={3}, Serial={4}, Image={5}",
this.GetType().Namespace.Substring(17),
cameraCfg.Name,
cameraCfg.HardwareAddress,
ipAddress == null ? "not detected" : ipAddress.ToString(),
string.IsNullOrEmpty(serial) ? "not detected" : serial,
string.IsNullOrEmpty(image) ? "not detected" : image);
}
readonly CameraCfg cameraCfg;
readonly GenericDevices.INetworkAdapter netAdapter;
public IPAddress IPAddress { get { return ipAddress; } }
IPAddress ipAddress;
public string Hardware { get { return hardware; } }
string hardware;
public string Revision { get { return revision; } }
string revision;
public string Serial { get { return serial; } }
string serial;
public string Image { get { return image; } }
string image;
Telnet.TelnetClient telnet;
TerminalDlg terminalDlg;
public Camera() {}
public Camera(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
{
cameraCfg = cfg as CameraCfg;
netAdapter = TbfComponents.FindComponent(cfg.ParentName, components) as GenericDevices.INetworkAdapter;
if (netAdapter == null) throw new ArgumentNullException("no network adapter");
log.Debug(this.ToString());
}
public void Initialize()
{
if (cameraCfg.DebugLevel == DebugMode.Simulate) return;
bool cameraDetected = DetectCamera(cameraCfg.HardwareAddress, true,
out ipAddress, out hardware, out revision, out serial, out image);
if (!cameraDetected) throw new Exception("No camera detected");
telnet = new Telnet.TelnetClient(this, cameraCfg.Name, "pi", "raspberry", "$ ");
if (cameraCfg.DisplayTerminal)
{
terminalDlg = new TerminalDlg(cameraCfg.Name, telnet);
terminalDlg.Show();
}
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.CONNECT, ipAddress.ToString(), 10, 30));
log.FatalFormat("Successfully initialized device {0}", ToString());
}
public void StopDevice()
{
if (terminalDlg != null)
{
terminalDlg.CloseDlg();
terminalDlg = null;
}
if (telnet != null)
{
telnet.Dispose();
telnet = null;
}
}
public void RunDeviceBefore() {}
public void RunDeviceAfter() {}
public IOperation LiveStreamOp()
{
return new LiveStreamOp(this, telnet);
}
/// <summary>
/// Detect the camera with specified hardware address (DIP switches)
/// </summary>
/// <param name="hwAddress">Hardware address (DIP switches)</param>
/// <param name="setDateTime">true = Broadcast 'SetDateTime' to all cameras</param>
/// <param name="ipAddress">Detected IP address</param>
/// <param name="hardware">Detected hardware</param>
/// <param name="revision">Detected HW revision</param>
/// <param name="serial">Detected serial number</param>
/// <param name="image">Detected OS image</param>
/// <returns>true when camera detected successfully</returns>
bool DetectCamera(int hwAddress, bool setDateTime, out IPAddress ipAddress, out string hardware, out string revision, out string serial, out string image)
{
for (int trials = 1; trials <= 3; trials++)
{
UdpClient udpClient = new UdpClient();
if (setDateTime)
{
SetDateTime(udpClient, DateTime.Now);
}
///
/// Send a request for camera information
///
DateTime detectionStart = DateTime.Now;
CameraInfoRequest(udpClient, hwAddress);
///
/// Receive a response or timeout
///
IAsyncResult asyncRslt = udpClient.BeginReceive(null, null);
if (asyncRslt.AsyncWaitHandle.WaitOne(500))
{
/// Response received within time limit
IPEndPoint cameraIPEndpoint = new IPEndPoint(IPAddress.Any, 1852);
byte[] inputBuffer = udpClient.EndReceive(asyncRslt, ref cameraIPEndpoint);
double duration_ms = (DateTime.Now - detectionStart).TotalMilliseconds;
ipAddress = cameraIPEndpoint.Address;
udpClient.Close();
string response = Encoding.ASCII.GetString(inputBuffer, 0, inputBuffer.Length);
string[] strArr = response.Split(new char[] { ' ', '=' });
if (strArr.Length == 10 && strArr[0] == "Address" && strArr[2] == "Hardware" &&
strArr[4] == "Revision" && strArr[6] == "Serial" && strArr[8] == "Image")
{
hardware = strArr[3];
revision = strArr[5];
serial = strArr[7];
image = strArr[9];
}
else if (strArr.Length == 8 && strArr[0] == "Address" && strArr[2] == "Hardware" &&
strArr[4] == "Revision" && strArr[6] == "Serial")
{
hardware = strArr[3];
revision = strArr[5];
serial = strArr[7];
image = string.Empty;
}
else
{
continue;
}
string msg = string.Format("Camera {0} detected in {1} ms: HWAddress={2} IPAddress={3} {4}",
cameraCfg.Name,
duration_ms,
cameraCfg.HardwareAddress,
ipAddress == null ? "not detected" : ipAddress.ToString(),
response);
log.Info(msg);
Console.WriteLine(msg);
return true; /// Camera detected
}
udpClient.Close();
}
ipAddress = null;
hardware = null;
revision = null;
serial = null;
image = null;
return false; /// No camera detected
}
void CameraInfoRequest(UdpClient udpClient, int hardwareAddress)
{
string strToSend = string.Format("getinfo {0}", hardwareAddress);
byte[] dataToSend = Encoding.ASCII.GetBytes(strToSend);
udpClient.Send(dataToSend, dataToSend.Length, new IPEndPoint(netAdapter.BroadcastAddress, 1852));
}
void SetDateTime(UdpClient udpClient, DateTime dateTime)
{
string strToSend = string.Format("setdatetime {0:yyMMddHHmmss}", dateTime);
byte[] dataToSend = Encoding.ASCII.GetBytes(strToSend);
udpClient.Send(dataToSend, dataToSend.Length, new IPEndPoint(netAdapter.BroadcastAddress, 1852));
}
}
}

View File

@ -0,0 +1,35 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public class CameraCfg : ComponentCfgBase, IChildComponentCfg
{
public IComponentCfgCtrl GetControl() { return new CameraCfgCtrl(); }
///
/// Serialized parameters
///
public int HardwareAddress;
public bool DisplayTerminal;
/// Private parameterless constructor invoked by all other (public) constructors
CameraCfg() { }
public CameraCfg(string name, IComponentFactory factory)
: this()
{
Name = name;
Factory = factory;
ParentName = "Network.Adapter";
HardwareAddress = 1;
}
public string ToString(int i)
{
return string.Format("Name={0}, HWAddr={1}, Parent={2}", Name, HardwareAddress, ParentName);
}
}
}

View File

@ -0,0 +1,109 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Windows.Forms;
using log4net;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public partial class CameraCfgCtrl : UserControl, IComponentCfgCtrl
{
static readonly ILog log = LogManager.GetLogger(typeof(CameraCfgCtrl));
ComponentParametersDlg parent;
public bool ShowMore { get { return false; } }
CameraCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as CameraCfg;
Redraw();
}
}
public CameraCfgCtrl()
{
InitializeComponent();
}
private void PumpCfgCtrl_Load(object sender, EventArgs e)
{
parent = ParentForm as ComponentParametersDlg;
if (parent == null) return;
if (parent.TbfComponents != null)
{
foreach (var cmpnt in parent.TbfComponents)
{
if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.BenchControl.Network.Adapter.Factory)
{
parentNameComboBox.Items.Add(cmpnt.Name);
}
}
}
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
classNameLabel.Text = config.Factory.ClassName;
nameTextBox.Text = config.Name;
parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName;
hwAddressTextBox.Text = config.HardwareAddress.ToString();
displayTerminalCheckBox.Checked = config.DisplayTerminal;
}
public void Unlock()
{
nameTextBox.Enabled = true;
parentNameComboBox.Enabled = true;
hwAddressTextBox.Enabled = true;
displayTerminalCheckBox.Enabled = true;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
config.ParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
config.HardwareAddress = int.Parse(hwAddressTextBox.Text);
config.DisplayTerminal = displayTerminalCheckBox.Checked;
return flags;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Parent Name'";
}
if (!int.TryParse(hwAddressTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'HW Address' should be between 0 and 63";
}
return flags;
}
}
}

View File

@ -0,0 +1,147 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
namespace TBF.BenchControl.Network.Camera.CLP1611
{
partial class CameraCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.hwAddressTextBox = new System.Windows.Forms.TextBox();
this.bitPositionLabel = new System.Windows.Forms.Label();
this.parentNameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.parentNameComboBox = new System.Windows.Forms.ComboBox();
this.displayTerminalCheckBox = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// hwAddressTextBox
//
this.hwAddressTextBox.Enabled = false;
this.hwAddressTextBox.Location = new System.Drawing.Point(122, 99);
this.hwAddressTextBox.Name = "hwAddressTextBox";
this.hwAddressTextBox.Size = new System.Drawing.Size(46, 20);
this.hwAddressTextBox.TabIndex = 6;
//
// bitPositionLabel
//
this.bitPositionLabel.AutoSize = true;
this.bitPositionLabel.Location = new System.Drawing.Point(36, 102);
this.bitPositionLabel.Name = "bitPositionLabel";
this.bitPositionLabel.Size = new System.Drawing.Size(67, 13);
this.bitPositionLabel.TabIndex = 5;
this.bitPositionLabel.Text = "HW Address";
//
// parentNameLabel
//
this.parentNameLabel.AutoSize = true;
this.parentNameLabel.Location = new System.Drawing.Point(36, 76);
this.parentNameLabel.Name = "parentNameLabel";
this.parentNameLabel.Size = new System.Drawing.Size(69, 13);
this.parentNameLabel.TabIndex = 3;
this.parentNameLabel.Text = "Parent Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(122, 47);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(130, 20);
this.nameTextBox.TabIndex = 2;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(36, 50);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 1;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(119, 21);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 0;
this.classNameLabel.Text = "ComonentName";
//
// parentNameComboBox
//
this.parentNameComboBox.Enabled = false;
this.parentNameComboBox.FormattingEnabled = true;
this.parentNameComboBox.Location = new System.Drawing.Point(122, 73);
this.parentNameComboBox.Name = "parentNameComboBox";
this.parentNameComboBox.Size = new System.Drawing.Size(130, 21);
this.parentNameComboBox.TabIndex = 4;
//
// displayTerminalCheckBox
//
this.displayTerminalCheckBox.AutoSize = true;
this.displayTerminalCheckBox.Enabled = false;
this.displayTerminalCheckBox.Location = new System.Drawing.Point(122, 128);
this.displayTerminalCheckBox.Name = "displayTerminalCheckBox";
this.displayTerminalCheckBox.Size = new System.Drawing.Size(99, 17);
this.displayTerminalCheckBox.TabIndex = 13;
this.displayTerminalCheckBox.Text = "Display terminal";
this.displayTerminalCheckBox.UseVisualStyleBackColor = true;
//
// CameraCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.displayTerminalCheckBox);
this.Controls.Add(this.parentNameComboBox);
this.Controls.Add(this.hwAddressTextBox);
this.Controls.Add(this.bitPositionLabel);
this.Controls.Add(this.parentNameLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "CameraCfgCtrl";
this.Size = new System.Drawing.Size(300, 200);
this.Load += new System.EventHandler(this.PumpCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox hwAddressTextBox;
private System.Windows.Forms.Label bitPositionLabel;
private System.Windows.Forms.Label parentNameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.ComboBox parentNameComboBox;
private System.Windows.Forms.CheckBox displayTerminalCheckBox;
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public class Factory : IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
public void ResetStaticProperties() { Camera.ResetStaticProperties(); }
public IComponent DummyComponent() { return new Camera(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new Camera(cfg, components); }
public IComponentCfg DefaultConfig() { return new CameraCfg(this.GetType().Namespace.Substring(17), this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(typeof(CameraCfg), component, this);
}
}
}

View File

@ -0,0 +1,49 @@
using TBF.BenchControl.Network.Telnet;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public class LiveStreamOp : IOperation
{
readonly Camera camera;
readonly Telnet.TelnetClient telnet;
bool liveStreamCommandSent;
/// <summary>
/// Events: Event.None or Event.Error
/// </summary>
/// <param name="camera">CLP1611.Camera reference</param>
public LiveStreamOp(Camera camera, Telnet.TelnetClient telnet)
{
this.camera = camera;
this.telnet = telnet;
}
public void Start()
{
liveStreamCommandSent = false;
}
public Event Run()
{
if (!liveStreamCommandSent)
{
if (telnet.State == TelnetClient.TelnetState.Inactive)
{
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_STRING, "clp/livestream.sh"));
liveStreamCommandSent = true;
}
}
return Event.None;
}
public void Stop()
{
if (liveStreamCommandSent)
{
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_COMMAND, Telnet.TelnetClient.CtrlCCommand));
}
}
}
}

View File

@ -0,0 +1,62 @@
namespace TBF.BenchControl.Network.Camera.CLP1611
{
partial class TerminalDlg
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.vtTextBox = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// vtTextBox
//
this.vtTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.vtTextBox.Location = new System.Drawing.Point(0, 0);
this.vtTextBox.Multiline = true;
this.vtTextBox.Name = "vtTextBox";
this.vtTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.vtTextBox.Size = new System.Drawing.Size(737, 437);
this.vtTextBox.TabIndex = 0;
//
// TerminalDlg
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(737, 437);
this.Controls.Add(this.vtTextBox);
this.Name = "TerminalDlg";
this.Text = "Terminal";
this.Load += new System.EventHandler(this.Terminal_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox vtTextBox;
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Windows.Forms;
using TBF.BenchControl.Network.Telnet;
namespace TBF.BenchControl.Network.Camera.CLP1611
{
public partial class TerminalDlg : Form
{
TelnetClient telnet;
bool dlgLoaded;
public TerminalDlg()
{
InitializeComponent();
}
public TerminalDlg(string name, TelnetClient telnet)
{
InitializeComponent();
Text = name;
this.telnet = telnet;
}
private void Terminal_Load(object sender, EventArgs e)
{
telnet.vtTextChangedHandler += delegate(object sndr, VtTextChangedEventArgs args)
{
if (InvokeRequired) { Invoke(new EventHandler<VtTextChangedEventArgs>(OnTextChanged), sndr, args); }
else OnTextChanged(sndr, args);
};
dlgLoaded = true;
}
void OnTextChanged(object sndr, VtTextChangedEventArgs args)
{
vtTextBox.Text = args.VtText;
}
delegate void CloseDlgDlgt(); /// Delegate of void Fn(void) function
public void CloseDlg()
{
if (dlgLoaded)
{
Invoke(new CloseDlgDlgt(OnCloseDlg));
}
}
void OnCloseDlg()
{
Close();
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.RoI
{
public class Factory : IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
public void ResetStaticProperties() { RoI.ResetStaticProperties(); }
public IComponent DummyComponent() { return new RoI(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new RoI(cfg, components); }
public IComponentCfg DefaultConfig() { return new RoICfg(this.GetType().Namespace.Substring(17), this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(typeof(RoICfg), component, this);
}
}
}

View File

@ -0,0 +1,32 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using log4net;
using Config.Entities;
namespace TBF.BenchControl.Network.Camera.RoI
{
public class RoI : ComponentBase
{
private static readonly ILog log = LogManager.GetLogger(typeof(RoI));
public override string ToString()
{
return string.Format("{0}({1})", this.GetType().Namespace.Substring(17), Cfg.ToString(1));
}
protected readonly RoICfg roiCfg;
public RoI()
{
}
public RoI(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
{
roiCfg = cfg as RoICfg;
log.Debug(this.ToString());
}
}
}

View File

@ -0,0 +1,34 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.RoI
{
public class RoICfg : ComponentCfgBase, IChildComponentCfg
{
public IComponentCfgCtrl GetControl() { return new RoICfgCtrl(); }
///
/// Serialized parameters
///
public int HardwareAddress;
/// Private parameterless constructor invoked by all other (public) constructors
RoICfg() { }
public RoICfg(string name, IComponentFactory factory)
: this()
{
Name = name;
Factory = factory;
ParentName = "Network.Camera";
HardwareAddress = 1;
}
public string ToString(int i)
{
return string.Format("Name={0}, HWAddr={1}", Name, HardwareAddress);
}
}
}

View File

@ -0,0 +1,106 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Windows.Forms;
using log4net;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.Network.Camera.RoI
{
public partial class RoICfgCtrl : UserControl, IComponentCfgCtrl
{
static readonly ILog log = LogManager.GetLogger(typeof(RoICfgCtrl));
ComponentParametersDlg parent;
public bool ShowMore { get { return false; } }
RoICfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as RoICfg;
Redraw();
}
}
public RoICfgCtrl()
{
InitializeComponent();
}
private void PumpCfgCtrl_Load(object sender, EventArgs e)
{
parent = ParentForm as ComponentParametersDlg;
if (parent == null) return;
if (parent.TbfComponents != null)
{
foreach (var cmpnt in parent.TbfComponents)
{
if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.BenchControl.Network.Camera.CLP1611.Factory)
{
parentNameComboBox.Items.Add(cmpnt.Name);
}
}
}
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
classNameLabel.Text = config.Factory.ClassName;
nameTextBox.Text = config.Name;
parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName;
hwAddressTextBox.Text = config.HardwareAddress.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
parentNameComboBox.Enabled = true;
hwAddressTextBox.Enabled = true;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
config.ParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
config.HardwareAddress = int.Parse(hwAddressTextBox.Text);
return flags;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Parent Name'";
}
if (!int.TryParse(hwAddressTextBox.Text, out dummy) || dummy < 1 || dummy > 63)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'HW Address' should be between 1 and 63";
}
return flags;
}
}
}

View File

@ -0,0 +1,133 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
namespace TBF.BenchControl.Network.Camera.RoI
{
partial class RoICfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.hwAddressTextBox = new System.Windows.Forms.TextBox();
this.bitPositionLabel = new System.Windows.Forms.Label();
this.parentNameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.parentNameComboBox = new System.Windows.Forms.ComboBox();
this.SuspendLayout();
//
// hwAddressTextBox
//
this.hwAddressTextBox.Enabled = false;
this.hwAddressTextBox.Location = new System.Drawing.Point(122, 99);
this.hwAddressTextBox.Name = "hwAddressTextBox";
this.hwAddressTextBox.Size = new System.Drawing.Size(46, 20);
this.hwAddressTextBox.TabIndex = 6;
//
// bitPositionLabel
//
this.bitPositionLabel.AutoSize = true;
this.bitPositionLabel.Location = new System.Drawing.Point(36, 102);
this.bitPositionLabel.Name = "bitPositionLabel";
this.bitPositionLabel.Size = new System.Drawing.Size(67, 13);
this.bitPositionLabel.TabIndex = 5;
this.bitPositionLabel.Text = "HW Address";
//
// parentNameLabel
//
this.parentNameLabel.AutoSize = true;
this.parentNameLabel.Location = new System.Drawing.Point(36, 76);
this.parentNameLabel.Name = "parentNameLabel";
this.parentNameLabel.Size = new System.Drawing.Size(69, 13);
this.parentNameLabel.TabIndex = 3;
this.parentNameLabel.Text = "Parent Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(122, 47);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(130, 20);
this.nameTextBox.TabIndex = 2;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(36, 50);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 1;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(119, 21);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 0;
this.classNameLabel.Text = "ComonentName";
//
// parentNameComboBox
//
this.parentNameComboBox.Enabled = false;
this.parentNameComboBox.FormattingEnabled = true;
this.parentNameComboBox.Location = new System.Drawing.Point(122, 73);
this.parentNameComboBox.Name = "parentNameComboBox";
this.parentNameComboBox.Size = new System.Drawing.Size(130, 21);
this.parentNameComboBox.TabIndex = 4;
//
// CameraCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.parentNameComboBox);
this.Controls.Add(this.hwAddressTextBox);
this.Controls.Add(this.bitPositionLabel);
this.Controls.Add(this.parentNameLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "CameraCfgCtrl";
this.Size = new System.Drawing.Size(300, 200);
this.Load += new System.EventHandler(this.PumpCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox hwAddressTextBox;
private System.Windows.Forms.Label bitPositionLabel;
private System.Windows.Forms.Label parentNameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.ComboBox parentNameComboBox;
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,124 @@
using System;
using System.Text.RegularExpressions;
namespace TBF.BenchControl.Network.Telnet
{
/// <summary>
/// One part of the telnet client command, which consists of a pair CmdAction action and string str.
/// </summary>
public enum CmdAction
{
VOID = 0, /// Blank (uninitialized) action. 'str' is not used.
CONNECT, /// Connect (=open a TCP socket and log in). 'str'=hostname. Notify by 'Connected' event.
SET_PROMPT, /// Set a prompt string used in comparisons when executing ..._WAIT4PROMPT. 'str'=prompt.
CLEAR_RESPONSE, /// Clear received response buffer. 'str' is not used. Note: Response buffer is cleared also after a detected prompt.
SEND_STRING, /// Send string and do nothing else. 'str'=string to be sent.
SEND_COMMAND, /// Send string and WAIT for a PROMPT. 'str'=string to be sent. Notify by 'PromptReceived' event.
REBOOT_RECONNECT, /// Send 'reboot' command (if connected), wait a while and reconect. 'str' is not used. Notify by 'Connected' event.
EXIT_RECONNECT, /// Send 'exit' (terminate telnet session) and reconect. 'str' is not used. Notify using 'Connected' event.
DISCONNECT, /// Send 'exit' to disconnect, do not reconnect. 'str' is not used.
TERMINATE, /// Terminate worker thread (should be done once before TelnetClient destruction. 'str' is not used.
LABEL, /// Label marking the start of item commands, 'str' identifies the item.
NOTIFICATION, /// Notify by 'Notification' event when this command is processed. 'str'=notification ID-string
}
/// <summary>
/// Telnet client command. Consists of a pair 'action', 'str'
/// CmdAction 'action' specifies the action to be taken.
/// string 'str' is (in most cases) the string to be sent, however it might be a hostname or a prompt.
/// </summary>
public struct Command
{
/// Private fields
CmdAction action; /// Telnet client action to be taken, e.g. 'send a command'
string str; /// String argument, e.g. a Linux command to be sent.
int duration; /// Expected duration of the command in [s], 0 = immediate, -1 = unknown
int timeout; /// Timeout period for the command in [s], 0 = no timeout
Regex fatalRegex; /// A compiled regular expression for a fatal error, or null (null=no fatal error detection)
Regex errorRegex; /// A compiled regular expression for an error, or null (null=no error detection)
Regex warningRegex; /// A compiled regular expression for a warning, or null (null=no warning detection)
Regex successRegex; /// A compiled regular expression for a success, or null (null=no success detection)
/// Public read only properties
public CmdAction Action { get { return action; } }
public string Str { get { return str; } }
public int Duration { get { return duration; } }
public int Timeout { get { return timeout; } }
public Regex FatalRegex { get { return fatalRegex; } }
public Regex ErrorRegex { get { return errorRegex; } }
public Regex WarningRegex { get { return warningRegex; } }
public Regex SuccessRegex { get { return successRegex; } }
///
/// Various constructors:
/// - duration and timeout are in seconds
/// - if timeout and duration is not supplied, timeout=0, duration=0 (timeout switched off)
/// - if string is not supplied, null is used
///
public Command(CmdAction action, string str, int duration, int timeout,
Regex fatalRegex, Regex errorRegex, Regex warningRegex, Regex successRegex)
{
this.action = action;
this.str = str;
this.duration = duration;
this.timeout = timeout;
this.fatalRegex = fatalRegex;
this.errorRegex = errorRegex;
this.warningRegex = warningRegex;
this.successRegex = successRegex;
}
public Command(CmdAction action, string str, int duration, int timeout)
: this(action, str, duration, timeout, null, null, null, null)
{
}
public Command(CmdAction action, string str)
: this(action, str, 0, 0, null, null, null, null)
{
}
public Command(CmdAction action, int duration, int timeout)
: this(action, null, duration, timeout, null, null, null, null)
{
}
public Command(CmdAction action)
: this(action, null, 0, 0, null, null, null, null)
{
}
/// <summary>
/// For debugging purposes.
/// </summary>
/// <returns>A name of Command.action</returns>
public override string ToString()
{
string result = action.ToString();
if (str != null)
{
result += ", \"" + str + "\"";
}
if (action == CmdAction.CONNECT ||
action == CmdAction.REBOOT_RECONNECT ||
action == CmdAction.EXIT_RECONNECT ||
action == CmdAction.SEND_COMMAND)
{
result += ", timeout=" + timeout.ToString() + "s";
}
if (action == CmdAction.SEND_COMMAND)
{
if (fatalRegex != null) { result += ", fatal=" + fatalRegex.ToString(); }
if (errorRegex != null) { result += ", error=" + errorRegex.ToString(); }
if (warningRegex != null) { result += ", warning=" + warningRegex.ToString(); }
if (successRegex != null) { result += ", success=" + successRegex.ToString(); }
}
return result;
}
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
namespace TBF.BenchControl.Network.Telnet
{
/// <summary>
/// Type of argument of FatalEventHandler
/// </summary>
public class FatalEventArgs : EventArgs
{
public string FatalErrorText; /// Fatal error description messages.
public FatalEventArgs(string text)
{
FatalErrorText = text;
}
}
/// <summary>
/// Type of argument of NotificationEventHandler
/// </summary>
public class NotificationEventArgs : EventArgs
{
public string NotificationText; /// String identifying this notification more closely.
public NotificationEventArgs(string text)
{
NotificationText = text;
}
}
/// <summary>
/// Type of argument of PromptReceviedEventHandler
/// </summary>
public class PromptReceivedEventArgs : EventArgs
{
public string Response; /// Response to a command from the telnet server ...
/// ... without the terminal prompt string.
public PromptReceivedEventArgs(string text)
{
Response = text;
}
}
/// <summary>
/// Type of argument of VtTextChangedEventHandler
/// </summary>
public class VtTextChangedEventArgs : EventArgs
{
public string VtText; // Virtual terminal text.
public VtTextChangedEventArgs(string text)
{
VtText = text;
}
}
/// <summary>
/// Used as a part of AuxBrdUpgItemMessage
/// </summary>
public enum Category
{
Error,
Warning,
Success,
}
/// <summary>
/// A pair containing Aux Board Upgrade item identification and a message
/// (fatal error, error, warning or success message).
/// </summary>
public class AuxBrdUpgItemMessage
{
public Category Category; /// Error, Warning or Success
public string Label; /// Identifies the aux.board upgrade item
public string Message; /// The message
public AuxBrdUpgItemMessage(Category category, string label, string message)
{
Category = category;
Label = label;
Message = message;
}
}
/// <summary>
/// Type of argument of SummaryEventArgs
/// </summary>
public class SummaryEventArgs : EventArgs
{
public IList<AuxBrdUpgItemMessage> SummaryMessages; /// Event description messages.
public SummaryEventArgs(IList<AuxBrdUpgItemMessage> summaryMessages)
{
this.SummaryMessages = summaryMessages;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,233 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Diagnostics;
namespace TBF.BenchControl.Network.Telnet
{
/// <summary>
/// Virtual terminal (an array of characters).
/// This class is thread safe under these circumstances:
/// - ClearScreen() and WriteVt() methods called from one thread only
/// - properties Text, etc. can be safely read from an arbitrary thread
/// </summary>
public class VirtualTerminal
{
const uint DefaultVtRowsCount = 80; /// Default VT height (number of rows)
const uint DefaultVtColumnsCount = 132; /// Default VT width (number of columns)
/// <summary>
/// Virtual terminal special characters.
/// <seealso cref = http://support.microsoft.com/kb/231866 />
/// </summary>
enum SpecialChar
{
BS = 8, /// 8 = Backspace (move one position to the left)
HT, /// 9 = Horizontal tab
LF, /// 10 = Line feed
VT, /// 11 = Vertical tab
FF, /// 12 = Form feed (on visual displays most commonly clears the screen)
CR /// 13 = Carriage return
}
///
/// Public properties
///
public uint Rows { get { return vtRows; } }
public uint Columns { get { return vtColumns; } }
public uint CurRow { get { return curRow; } }
public uint CurColumn { get { return curColumn; } }
/// <summary>
/// Virtual terminal text buffer as a single string. To be printed out using monospaced font.
/// </summary>
public string Text
{
get
{
string result = "";
char[] oneRow = new char[vtColumns];
for (int row = 0; row < vtRows; row++)
{
for (int column = 0; column < vtColumns; column++)
{
oneRow[column] = vt[row, column];
}
result += new String(oneRow);
/// New line at the end of each line except of the last one
if (row < vtRows - 1) result += System.Environment.NewLine;
}
return result;
}
}
///
/// Private fields
///
char[,] vt; /// Virtual terminal text buffer
uint vtRows = 0; /// VT height (number of rows)
uint vtColumns = 0; /// VT width (number of columns)
uint curRow = 0; /// Current row (cursor position)
uint curColumn = 0; /// Current column (cursor position)
/// <summary>
/// Default constructor.
/// </summary>
public VirtualTerminal()
{
vt = new char[DefaultVtRowsCount, DefaultVtColumnsCount];
if (vt != null)
{
vtRows = DefaultVtRowsCount;
vtColumns = DefaultVtColumnsCount;
ClearScreen();
}
}
/// <summary>
/// Arbitrary VT size constructor.
/// </summary>
public VirtualTerminal(uint nrRows, uint nrColumns)
{
vt = new char[nrRows, nrColumns];
if (vt != null)
{
vtRows = nrRows;
vtColumns = nrColumns;
ClearScreen();
}
}
/// <summary>
/// Clear VT.
/// </summary>
public void ClearScreen()
{
for (uint row = 0; row < vtRows; row++)
{
for (uint column = 0; column < vtColumns; column++)
{
vt[row, column] = ' ';
}
}
curRow = 0;
curColumn = 0;
}
/// <summary>
/// Scroll VT one line up and clear the bottom line.
/// </summary>
void ScrollUp()
{
for (uint row = 0; row < vtRows - 1; row++)
{
for (uint column = 0; column < vtColumns; column++)
{
vt[row, column] = vt[row + 1, column];
}
}
for (uint column = 0; column < vtColumns; column++)
{
vt[vtRows - 1, column] = ' ';
}
}
/// <summary>
/// Write one regular printable character to VC.
/// </summary>
void WritePrintableChar(char c)
{
vt[curRow, curColumn] = c;
if (curColumn < vtColumns - 1)
{
curColumn++;
}
else if (curRow < vtRows - 1)
{
curRow++;
curColumn = 0;
}
else
{
ScrollUp();
curRow = vtRows - 1;
curColumn = 0;
}
}
/// <summary>
/// Write one character to VT (regular or special).
/// </summary>
public void WriteVt(char c)
{
if (c >= 32 && c <= 127)
{
WritePrintableChar(c);
}
else
{
switch ((SpecialChar)c)
{
/// Carriage return
case SpecialChar.CR:
curColumn = 0;
break;
/// Line feed
case SpecialChar.LF:
if (curRow < vtRows - 1)
{
curRow++;
}
else
{
ScrollUp();
curRow = vtRows - 1;
}
break;
/// Form feed (= clear screen)
case SpecialChar.FF:
ClearScreen();
break;
/// Backspace
case SpecialChar.BS:
if (curColumn > 0)
{
vt[curRow, --curColumn] = ' ';
}
else if (curRow > 0)
{
curColumn = vtColumns - 1;
vt[--curRow, curColumn] = ' ';
}
break;
default:
break;
}
}
}
/// <summary>
/// Write a string to VT.
/// </summary>
public void WriteVt(string txt)
{
if (txt != null)
{
for (int i = 0; i < txt.Length; i++)
{
WriteVt(txt[i]);
}
}
}
}
}

View File

@ -0,0 +1,688 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using log4net;
namespace TBF.BenchControl.Network.Tftp
{
/// <summary>
/// Simple TFTP Server
/// </summary>
public class TftpServer
{
static readonly ILog log = LogManager.GetLogger("TftpServer");
const ushort DefaultTftpPortNr = 69;
const int TftpHeaderLength = 4;
const int TftpDataBlockLength = 512;
readonly char[] Delimiter = new char[] { '\0' }; /// Delimits filename, transfer mode and error message in UDP packets
/// <summary>
/// TFTP server request modes
/// </summary>
enum RequestMode
{
BinaryRead,
BinaryWrite,
AsciiRead,
AsciiWrite,
}
/// <summary>
/// Opcodes identifying TFTP packets
/// </summary>
enum Opcode : short
{
RRQ = 01, /// Read request packet.
WRQ = 02, /// Write request packet.
DATA = 03, /// Data packet.
ACK = 04, /// Acknowledgement packet.
ERROR = 05, /// Wrror packet.
OACK = 06 /// Option acknowledgement packet.
}
/// Strings included in RRQ/WRQ packets
const string REQUEST_MODE_NETASCII = "netascii";
const string REQUEST_MODE_BINARY = "octet";
/// <summary>
/// Error codes included in ERROR packet
/// </summary>
enum ErrorCode : short
{
NO_ERROR = 0, /// Not defined, see error message (if any).
ERROR_FILE_NOT_FOUND = 1, /// File not found.
ERROR_ACCESS_VIOLATION = 2, /// Access violation.
ERROR_ALLOC_ERROR = 3, /// Disk full or allocation exceeded.
ERROR_ILLEGAL_OP = 4, /// Illegal TFTP operation.
ERROR_UNKNOWN_TID = 5, /// Unknown transfer ID.
ERROR_FILE_EXISTS = 6, /// File already exists.
ERROR_INVALID_USER = 7, /// No such user.
}
///
/// Single file transfer related class
///
class FileTransfer
{
public IPEndPoint Endpoint { get { return endpoint; } }
IPEndPoint endpoint;
public string Filename { get { return filename; } }
string filename;
public RequestMode Mode { get { return mode; } }
RequestMode mode;
public System.IO.Stream Stream;
public BinaryReader StreamReader;
public BinaryWriter StreamWriter;
public int CurrentBlock;
public int LastSentDataLength;
public FileTransfer(IPEndPoint endpoint, string filename, RequestMode mode)
{
this.endpoint = endpoint;
this.filename = filename;
this.mode = mode;
Stream = null;
StreamReader = null;
StreamWriter = null;
CurrentBlock = (mode == RequestMode.BinaryRead ? 1 : 0);
LastSentDataLength = 0;
}
/// <summary>
/// IPEndPoint can be used as a key as there is max. one file transfer for each endpoint.
/// </summary>
/// <param name="ep">IP endpoint</param>
/// <param name="list">List of FileTransfer-s</param>
/// <returns></returns>
public static FileTransfer Find(IPEndPoint ep, IList<FileTransfer> list)
{
if (list == null) return null;
foreach (FileTransfer ft in list)
{
if ((ft.endpoint.Address.Equals(ep.Address)) && (ft.endpoint.Port.Equals(ep.Port)))
{
return ft;
}
}
return null;
}
/// <summary>
/// FileTransfer Close() closes open streams.
/// </summary>
public void Close()
{
if (StreamReader != null) StreamReader.Close();
if (StreamWriter != null) StreamWriter.Close();
if (Stream != null) Stream.Close();
}
/// For debugging.
public override string ToString()
{
return "[" + endpoint.ToString() + "," + filename + "," + mode.ToString() +
",block=" + CurrentBlock.ToString() + ",last=" + LastSentDataLength.ToString() + "]";
}
}
///
/// TFTP server instance related instance fields
///
string tftpDirectory; /// TFTP root directory
int listenPort; /// UDP port to listen to
UdpClient udpClient;
Thread listenThread;
bool done; /// Flag to finish listenThread
IList<FileTransfer> fileTransfers; /// Current file transfers (max. one transfer for each endpoint)
public TftpServer()
{
tftpDirectory = null;
udpClient = null; /// UDP client
listenThread = null; /// Listen thread
done = false; /// Flag to finish listenThread
fileTransfers = null; /// No file transfer
}
#region Event handler
public class NotificationEventArgs : EventArgs
{
public string Message;
public IPAddress IP;
public string FileName;
public NotificationEventArgs(string message, IPAddress ip, string filename)
{
this.Message = message;
this.IP = ip;
this.FileName = filename;
}
}
public delegate void NotificationEventHandler(object sender, NotificationEventArgs args);
public static event NotificationEventHandler NotificationHandler;
/// <summary>
/// Writes a log and invokes notification handlers (updates TFTP tab-page).
/// </summary>
/// <param name="message">Notification text start</param>
/// <param name="fileTransfer">File transfer object reference or null</param>
/// <param name="logType">Logger log type </param>
void OnNotify(string message, FileTransfer fileTransfer, log4net.Core.Level level)
{
/// Write a log
IPAddress ip = null;
string filename = null;
string logStr = message;
if (fileTransfer != null)
{
ip = fileTransfer.Endpoint.Address;
filename = fileTransfer.Filename;
logStr = string.Format("{0} IP={1}, filename={2}", message, ip, filename);
}
if (level == log4net.Core.Level.Debug)
log.Debug(logStr);
else if (level == log4net.Core.Level.Info)
log.Info(logStr);
else if (level == log4net.Core.Level.Warn)
log.Warn(logStr);
else if (level == log4net.Core.Level.Error)
log.Error(logStr);
else if (level == log4net.Core.Level.Fatal || level == log4net.Core.Level.All)
log.Fatal(logStr);
/// Notification: invoke handlers
if (NotificationHandler != null)
{
NotificationHandler(null, new NotificationEventArgs(message, ip, filename));
}
}
#endregion
/// <summary>
/// Start TFTP server
/// </summary>
/// <param name="tftpDirectory">TFTP root directory</param>
/// <param name="netAdapterInfo">Network adadpter to listen to, null=listen to all net.adapters</param>
/// <returns>true = TFTP is running, false = TFTP start failed</returns>
public bool Start(string tftpDirectory, IPAddress ipAddress)
{
if (!Directory.Exists(tftpDirectory)) return false; /// TFTP root directory does not exist
if (!tftpDirectory.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
tftpDirectory += Path.DirectorySeparatorChar.ToString();
}
this.tftpDirectory = tftpDirectory;
this.listenPort = DefaultTftpPortNr;
if (udpClient != null) return false; /// TFTP server is already running
try
{
if (ipAddress != null)
{
IPEndPoint listenEndpoint = new IPEndPoint(ipAddress, listenPort);
udpClient = new UdpClient(listenEndpoint);
}
else
{
udpClient = new UdpClient(listenPort, AddressFamily.InterNetwork);
}
}
catch
{
return false; /// Cannot open UDP port (is another TFTP running?)
}
try
{
fileTransfers = new List<FileTransfer>();
listenThread = new Thread(new ThreadStart(Listener));
listenThread.Name = "TftpServer"; /// Is used as a log file root name
listenThread.Start();
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Stop TFTP server
/// </summary>
public void Stop()
{
done = true;
if (udpClient != null)
{
udpClient.Close();
udpClient = null;
}
try
{
/// Close the open file streams
if (fileTransfers != null)
{
foreach (FileTransfer ft in fileTransfers) ft.Close();
}
}
catch (Exception ex)
{
log.FatalFormat("Fatal error in Stop(): {0}", ex.Message);
}
fileTransfers = null;
}
#region Listner thread section
/// <summary>
/// Get readable string describing the packet.
/// </summary>
/// <param name="data">TFTP packet</param>
/// <param name="actualLength">Number of valid byted in array data</param>
/// <returns>description</returns>
string GetDebugMessage(Byte[] data, int actualLength)
{
if (data == null || data.Length < actualLength || actualLength < TftpHeaderLength) return "[invalid packet]";
Opcode opcode = (Opcode)((((short)data[0]) * 256) + (short)data[1]);
if (opcode == Opcode.RRQ || opcode == Opcode.WRQ)
{
Encoding ASCII = Encoding.ASCII;
string[] strData = ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
string filename = strData[0];
string mode = strData[1].ToLower();
return "[" + opcode.ToString() + " filename=" + filename + ", mode=" + mode + "]";
}
int number = data[2] * 256 + data[3];
if (actualLength > TftpHeaderLength)
{
return "[" + opcode.ToString() + " + " + number.ToString() + " + " + (actualLength - TftpHeaderLength).ToString() + " bytes]";
}
else
{
return "[" + opcode.ToString() + " + " + number.ToString() + "]";
}
}
/// <summary>
/// Main listener thread loop
/// </summary>
public void Listener()
{
OnNotify("----- TFTP server started -----", null, log4net.Core.Level.Fatal);
while (!done)
{
try
{
IPEndPoint endpoint = null;
Byte[] data = udpClient.Receive(ref endpoint);
log.InfoFormat("Rcvd packet {0}", GetDebugMessage(data, data.Length));
///
/// Process the packet
///
Opcode opcode = (Opcode)((((short)data[0]) * 256) + (short)data[1]);
///
/// Find any existing file transfer with this IP endpoint
///
FileTransfer fileTransfer = FileTransfer.Find(endpoint, fileTransfers);
if (opcode == Opcode.RRQ || opcode == Opcode.WRQ)
{
if (fileTransfer != null)
{
///
/// If there is an incomplete file transfer with this endpoint, close the open streams.
///
OnNotify("Forcing an incomplete file transfer to close: ", fileTransfer, log4net.Core.Level.Error);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
fileTransfer = null;
}
}
else if (fileTransfer == null)
{
/// If there is no existing file transfer with this endpoint, ignore packets other then RRQ or WRQ.
log.ErrorFormat("No matching file transfer - packet ignored: IP={0}", endpoint.Address);
continue;
}
switch (opcode)
{
case Opcode.RRQ:
ProcessReadRequest(data, endpoint);
break;
case Opcode.WRQ:
ProcessWriteRequest(data, endpoint);
break;
case Opcode.ERROR:
ProcessError(data, fileTransfer);
break;
case Opcode.ACK:
ProcessAck(data, fileTransfer);
break;
case Opcode.DATA:
ProcessData(data, fileTransfer);
break;
case Opcode.OACK:
default:
break;
}
}
catch (Exception exc)
{
log.FatalFormat("Fatal error in Listener(): {0}", exc.Message);
}
}
return;
}
#region TFTP 'GET' section
/// <summary>
/// Handle 'Read Request' (RRQ)
/// </summary>
/// <param name="data">Data from RRQ packet</param>
/// <param name="endpoint">Client IP</param>
private void ProcessReadRequest(Byte[] data, IPEndPoint endpoint)
{
/// Extract a filename and a mode
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
string filename = strData[0];
string mode = strData[1].ToLower();
if (mode == REQUEST_MODE_BINARY)
{
/// Create FileTransfer object, do not add it to this.fileTransfers yet
FileTransfer fileTransfer = new FileTransfer(endpoint, filename, RequestMode.BinaryRead);
/// Try to open the source file
try
{
fileTransfer.Stream = System.IO.File.OpenRead(tftpDirectory + filename);
fileTransfer.StreamReader = new BinaryReader(fileTransfer.Stream);
}
catch
{
OnNotify("Cannot open file to be uploaded: ", fileTransfer, log4net.Core.Level.Warn);
SendError(fileTransfer, ErrorCode.ERROR_FILE_NOT_FOUND);
return;
}
/// File open OK, start the transfer, send the 1st data packet
OnNotify("Uploading file: ", fileTransfer, log4net.Core.Level.Info);
fileTransfers.Add(fileTransfer);
SendData(fileTransfer);
}
else
{
throw (new Exception("Built-in TFTP server does not support '" + mode + "' file transfer mode."));
}
}
/// <summary>
/// Send part of file data
/// </summary>
/// <param name="endpoint">location to send stream to</param>
/// <param name="blockNumber">512 byte block to send</param>
private void SendData(FileTransfer fileTransfer)
{
int fileOffset = (fileTransfer.CurrentBlock - 1) * TftpDataBlockLength;
fileTransfer.Stream.Seek(fileOffset, SeekOrigin.Begin);
/// Prepare data buffer
Byte[] buffer = new Byte[TftpDataBlockLength + TftpHeaderLength];
buffer[0] = 0;
buffer[1] = (byte)Opcode.DATA;
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0x0000FF00) >> 8);
buffer[3] = (byte) (fileTransfer.CurrentBlock & 0x000000FF);
fileTransfer.LastSentDataLength = fileTransfer.StreamReader.Read(buffer, TftpHeaderLength, TftpDataBlockLength);
/// Send the data packet
int ecode = udpClient.Send(buffer, fileTransfer.LastSentDataLength + TftpHeaderLength, fileTransfer.Endpoint);
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, fileTransfer.LastSentDataLength + TftpHeaderLength), ecode);
/// Check the return value
if (ecode != fileTransfer.LastSentDataLength + TftpHeaderLength)
{
OnNotify("Error when sending data: ecode = " + ecode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
}
}
/// <summary>
/// Handle ACK response and send next block.
/// </summary>
/// <param name="data">data from packet</param>
/// <param name="endpoint">client</param>
private void ProcessAck(Byte[] data, FileTransfer fileTransfer)
{
int protocolBlocknum = 256 * (int)data[2] + (int)data[3];
/// Check the received block number, support for file size 2GB
if (protocolBlocknum == (fileTransfer.CurrentBlock & 0xFFFF))
{
/// OK => Check the last data block length
if (fileTransfer.LastSentDataLength < TftpDataBlockLength)
{
/// Nothing more to send, finish the transfer
OnNotify("Upload completed: ", fileTransfer, log4net.Core.Level.Info);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
}
else
{
/// Send the next data block
fileTransfer.CurrentBlock++;
SendData(fileTransfer);
}
}
else
{
/// Block number NOK => re-send the last block (without incrementing fileTransfer.CurrentBlock).
/// This often happens when transferring large files due to iCOM performance limits.
log.ErrorFormat("Received ACK does not match the sent block#: IP={0} ---> Re-sending", fileTransfer.Endpoint.Address);
SendData(fileTransfer);
}
}
#endregion
#region TFTP 'PUT' section
/// <summary>
/// Handle 'Write Request' (WRQ)
/// </summary>
/// <param name="data">Data from WRQ packet</param>
/// <param name="endpoint">Client IP</param>
private void ProcessWriteRequest(Byte[] data, IPEndPoint endpoint)
{
/// Extract a filename and a mode
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
string filename = strData[0];
string mode = strData[1].ToLower();
if (mode == REQUEST_MODE_BINARY)
{
/// Create FileTransfer object, do not add it to this.fileTransfers yet
FileTransfer fileTransfer = new FileTransfer(endpoint, filename, RequestMode.BinaryWrite);
/// Try to open the source file
try
{
fileTransfer.Stream = System.IO.File.Create(tftpDirectory + filename);
fileTransfer.StreamWriter = new BinaryWriter(fileTransfer.Stream);
}
catch
{
OnNotify("Cannot open file to be downloaded for writing: ", fileTransfer, log4net.Core.Level.Warn);
SendError(fileTransfer, ErrorCode.ERROR_FILE_EXISTS);
return;
}
/// File open OK, start the transfer, send the 0th acknowledge packet
OnNotify("Downloading file: ", fileTransfer, log4net.Core.Level.Info);
fileTransfers.Add(fileTransfer);
SendAck(fileTransfer);
}
else
{
throw (new Exception("Built-in TFTP server does not support '" + mode + "' file transfer mode."));
}
}
/// <summary>
/// Send acknowledge packet (ACK)
/// </summary>
/// <param name="fileTransfer">Current FileTransfer object</param>
private void SendAck(FileTransfer fileTransfer)
{
const int AcknowledgePacketLength = 4;
/// Prepare the acknowledgment packet
Byte[] buffer = new Byte[AcknowledgePacketLength];
buffer[0] = 0;
buffer[1] = (byte)Opcode.ACK;
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0x0000FF00) >> 8);
buffer[3] = (byte) (fileTransfer.CurrentBlock & 0x000000FF);
/// Send the packet
int ecode = udpClient.Send(buffer, AcknowledgePacketLength, fileTransfer.Endpoint);
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, AcknowledgePacketLength), ecode);
/// Check the return value
if (ecode != AcknowledgePacketLength)
{
OnNotify("Error when sending ACK packet: ecode = " + ecode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
}
}
private void ProcessData(Byte[] data, FileTransfer fileTransfer)
{
int protocolBlocknum = 256 * (int)data[2] + (int)data[3];
/// Check the received data block number, support max. file size 2GB
if (protocolBlocknum != ((fileTransfer.CurrentBlock + 1) & 0xFFFF))
{
OnNotify("Received data block # is not consecutive --> aborting transfer: ", fileTransfer, log4net.Core.Level.Error);
SendError(fileTransfer, ErrorCode.ERROR_UNKNOWN_TID);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
return;
}
/// Update the current block number, save and acknowledge data
fileTransfer.CurrentBlock++;
for (int i = TftpHeaderLength; i < data.Length; i++) fileTransfer.StreamWriter.Write(data[i]);
SendAck(fileTransfer);
/// Close the file transfer if the block was the last one (data size < 512)
if (data.Length < TftpDataBlockLength + TftpHeaderLength)
{
OnNotify("Download completed: ", fileTransfer, log4net.Core.Level.Info);
fileTransfer.Close();
fileTransfers.Remove(fileTransfer);
}
}
#endregion
/// <summary>
/// Send part of a stream
/// </summary>
/// <param name="endpoint">location to send stream to</param>
/// <param name="BlockNumber">512 byte block to send</param>
private void SendError(FileTransfer fileTransfer, ErrorCode errorCode)
{
OnNotify("Sending Error Packet: errorCode = " + errorCode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
const int ErrorPacketLength = 10;
Byte[] buffer = new Byte[ErrorPacketLength];
buffer[0] = 0;
buffer[1] = (byte)Opcode.ACK;
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0xFF00) / 256);
buffer[3] = (byte)(fileTransfer.CurrentBlock & 0x00FF);
buffer[4] = (Byte)'E';
buffer[5] = (Byte)'r';
buffer[6] = (Byte)'r';
buffer[7] = (Byte)'o';
buffer[8] = (Byte)'r';
buffer[9] = 0;
int ecode = udpClient.Send(buffer, ErrorPacketLength, fileTransfer.Endpoint);
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, ErrorPacketLength), ecode);
if (ecode <= 0)
{
Debug.WriteLine("Error in send : {0}", ecode);
}
}
/// <summary>
/// Parse an error response
/// </summary>
/// <param name="data">data from packet</param>
/// <param name="endpoint">client</param>
private void ProcessError(Byte[] data, FileTransfer fileTransfer)
{
int errorCode = 256 * (int)data[2] + (int)data[3];
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
string message = strData[0];
OnNotify("Received Error Packet: errorCode=" + errorCode.ToString() + ", message=" + message + ", ",
fileTransfer, log4net.Core.Level.Info);
}
#endregion
}
}

View File

@ -71,6 +71,9 @@ namespace TBF.BenchControl
Factories.Add(new MettlerToledo.Multi.BalanceFactory()); /// MettlerToledo-Multi
Factories.Add(new Modbus.Common.Factory()); /// Modbus
Factories.Add(new Modbus.PressureMeter.Meret.Factory()); /// Meret pressure meter connected via modbus
Factories.Add(new Network.Adapter.Factory());
Factories.Add(new Network.Camera.CLP1611.Factory());
Factories.Add(new Network.Camera.RoI.Factory());
Factories.Add(new Output.FileWriters.Basic.FactorySingle()); /// Output.FileWriters.Basic.Single
Factories.Add(new Output.FileWriters.Basic.FactoryCompound()); /// Output.FileWriters.Basic.Compound
Factories.Add(new Output.FileWriters.Basic.FactoryHeatMeters()); /// Output.FileWriters.Basic.FactoryHeatMeters

View File

@ -131,6 +131,7 @@
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Numerics" />
<Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
@ -409,6 +410,7 @@
<Compile Include="BenchControl\GenericDevices\IHasHeatMtrStatesForm.cs" />
<Compile Include="BenchControl\GenericDevices\IModbus.cs" />
<Compile Include="BenchControl\GenericDevices\IBenchInfo.cs" />
<Compile Include="BenchControl\GenericDevices\INetworkAdapter.cs" />
<Compile Include="BenchControl\GenericDevices\ISimultTestMethod.cs" />
<Compile Include="BenchControl\HeatMetersPath.cs" />
<Compile Include="BenchControl\Keithley\Multimeter_2010_RS232\Factory.cs" />
@ -492,6 +494,46 @@
</Compile>
<Compile Include="BenchControl\Modbus\QuidoRS\Factory.cs" />
<Compile Include="BenchControl\Modbus\QuidoRS\SetOutputsOp.cs" />
<Compile Include="BenchControl\Network\Adapter\AdapterInfo.cs" />
<Compile Include="BenchControl\Network\Adapter\Netadapter.cs" />
<Compile Include="BenchControl\Network\Adapter\Factory.cs" />
<Compile Include="BenchControl\Network\Adapter\NetadapterCfg.cs" />
<Compile Include="BenchControl\Network\Adapter\NetadapterCfgCtrl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="BenchControl\Network\Adapter\NetadapterCfgCtrl.designer.cs">
<DependentUpon>NetadapterCfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="BenchControl\Network\Camera\CLP1611\Camera.cs" />
<Compile Include="BenchControl\Network\Camera\CLP1611\CameraCfg.cs" />
<Compile Include="BenchControl\Network\Camera\CLP1611\CameraCfgCtrl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="BenchControl\Network\Camera\CLP1611\CameraCfgCtrl.designer.cs">
<DependentUpon>CameraCfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="BenchControl\Network\Camera\CLP1611\Factory.cs" />
<Compile Include="BenchControl\Network\Camera\CLP1611\LiveStreamOp.cs" />
<Compile Include="BenchControl\Network\Camera\CLP1611\TerminalDlg.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="BenchControl\Network\Camera\CLP1611\TerminalDlg.Designer.cs">
<DependentUpon>TerminalDlg.cs</DependentUpon>
</Compile>
<Compile Include="BenchControl\Network\Camera\RoI\RoI.cs" />
<Compile Include="BenchControl\Network\Camera\RoI\RoICfg.cs" />
<Compile Include="BenchControl\Network\Camera\RoI\RoICfgCtrl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="BenchControl\Network\Camera\RoI\RoICfgCtrl.designer.cs">
<DependentUpon>RoICfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="BenchControl\Network\Camera\RoI\Factory.cs" />
<Compile Include="BenchControl\Network\Telnet\Command.cs" />
<Compile Include="BenchControl\Network\Telnet\EventArgsClasses.cs" />
<Compile Include="BenchControl\Network\Telnet\TelnetClient.cs" />
<Compile Include="BenchControl\Network\Telnet\VirtualTerminal.cs" />
<Compile Include="BenchControl\Network\Tftp\TftpServer.cs" />
<Compile Include="BenchControl\Operations\EnduranceDataLoggingOp.cs" />
<Compile Include="BenchControl\Operations\EnthalpyCalculationOp.cs" />
<Compile Include="BenchControl\Output\FileWriters\Basic\FactoryCompound.cs" />
@ -1790,6 +1832,18 @@
<EmbeddedResource Include="BenchControl\Modbus\QuidoRS\QuidoRSCfgCtrl.resx">
<DependentUpon>QuidoRSCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="BenchControl\Network\Adapter\NetadapterCfgCtrl.resx">
<DependentUpon>NetadapterCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="BenchControl\Network\Camera\CLP1611\CameraCfgCtrl.resx">
<DependentUpon>CameraCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="BenchControl\Network\Camera\CLP1611\TerminalDlg.resx">
<DependentUpon>TerminalDlg.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="BenchControl\Network\Camera\RoI\RoICfgCtrl.resx">
<DependentUpon>RoICfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="BenchControl\Operations\AskYesNoForm.resx">
<DependentUpon>AskYesNoForm.cs</DependentUpon>
</EmbeddedResource>