Refactorization : BenchControl -> Rig
This commit is contained in:
@@ -0,0 +1,839 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using log4net;
|
||||
using Renci.SshNet;
|
||||
using Config.Entities;
|
||||
using TBF.Rig.Network.Telnet;
|
||||
using TBF.Resources;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements: (1) Camera device, (2) Measurement operation
|
||||
/// </summary>
|
||||
public class Camera : ComponentBase, GenericDevices.ICamera, Generic.IDevice, IOperation
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(Camera));
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}: Name={1}, Addr={2}, IP={3}, s/n={4}, SD={5}",
|
||||
this.GetType().Namespace.Substring(8),
|
||||
CameraCfg.Name,
|
||||
CameraCfg.HardwareAddress,
|
||||
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : ((ipAddress == null) ? "not detected" : ipAddress.ToString()),
|
||||
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : (string.IsNullOrEmpty(cpuSerialNr) ? "not detected" : cpuSerialNr),
|
||||
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : (string.IsNullOrEmpty(sdCardVer) ? "not detected" : sdCardVer));
|
||||
}
|
||||
|
||||
|
||||
string ClpUserName = string.Empty; /// Either "tbf" or "pi" after a successful detection
|
||||
const string ClpPassword = "C1ern4V0d4";
|
||||
|
||||
private static Object fileUploadLock = new Object();
|
||||
|
||||
|
||||
///
|
||||
/// Properties
|
||||
///
|
||||
public readonly CameraCfg CameraCfg;
|
||||
public readonly GenericDevices.INetworkAdapter NetAdapter;
|
||||
|
||||
public IPAddress IPAddress { get { return ipAddress; } }
|
||||
IPAddress ipAddress;
|
||||
|
||||
public string CpuHardware { get { return cpuHardware; } }
|
||||
string cpuHardware;
|
||||
|
||||
public string CpuRevision { get { return cpuRevision; } }
|
||||
string cpuRevision;
|
||||
|
||||
public string CpuSerialNr { get { return cpuSerialNr; } }
|
||||
string cpuSerialNr;
|
||||
|
||||
public string SDCardVer { get { return sdCardVer; } }
|
||||
string sdCardVer;
|
||||
|
||||
public bool Running { get { return running; } }
|
||||
bool running;
|
||||
|
||||
public int CameraIdx { get { return cameraIdx; } }
|
||||
int cameraIdx;
|
||||
///
|
||||
static int nextCameraIdx = 0;
|
||||
static int GetCameraIdx() { return nextCameraIdx++; } /// Used in Initialize()
|
||||
public static new void ResetStaticProperties() { nextCameraIdx = 0; }
|
||||
|
||||
///
|
||||
/// Telnet support
|
||||
///
|
||||
const string TelnetPrompt = "$ ";
|
||||
const bool TelnetSkipFirstLine = true;
|
||||
public Telnet.TelnetClient Telnet;
|
||||
private TerminalDlg terminalDlg;
|
||||
string sha1sumResponse;
|
||||
bool sha1sumResponseProcessed;
|
||||
|
||||
///
|
||||
/// scp support
|
||||
///
|
||||
Thread scpThread;
|
||||
PasswordConnectionInfo connectionInfo;
|
||||
bool scpConnected;
|
||||
bool stopScpThreadFlag;
|
||||
string scpRemoteFileName; /// Camera file name
|
||||
string scpLocalFileName; /// Local PC file name
|
||||
enum ScpCommand
|
||||
{
|
||||
None,
|
||||
Get,
|
||||
Put,
|
||||
}
|
||||
ScpCommand scpCommand;
|
||||
|
||||
|
||||
const int UdpPortBase = 20000; /// Port base for all UDP protocols
|
||||
|
||||
///
|
||||
/// MJpeg RTP protocol support
|
||||
///
|
||||
int rtpUdpLocalPort;
|
||||
UdpClient rtpUdpClient;
|
||||
Thread rtpListenerThread;
|
||||
static bool stopRtpListenerFlag;
|
||||
|
||||
///
|
||||
/// RTCP protocol (reserved)
|
||||
///
|
||||
int rtcpUdpLocalPort;
|
||||
|
||||
///
|
||||
/// Measurement results protocol
|
||||
///
|
||||
int msrmntUdpLocalPort;
|
||||
UdpClient msrmntUdpClient; /// If != null msrmntListenerThread
|
||||
Thread msrmntListenerThread;
|
||||
static bool stopMsrmntListenerFlag;
|
||||
|
||||
|
||||
///
|
||||
/// ROI related parameters
|
||||
///
|
||||
RoisAndResults roisAndResults;
|
||||
///
|
||||
public void ClearRoiParams() { roisAndResults.ClearRoiParams(); }
|
||||
public int RegisterRoi(string roiParams) { return roisAndResults.RegisterRoi(roiParams); }
|
||||
public int GetResult(int roiHandle, out long timeMs) { return roisAndResults.GetResult(roiHandle, out timeMs); }
|
||||
|
||||
|
||||
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");
|
||||
|
||||
Telnet = null;
|
||||
terminalDlg = null;
|
||||
|
||||
roisAndResults = new RoisAndResults(); /// object for measurement results, writes/reads to/from this object are locked
|
||||
|
||||
running = false;
|
||||
}
|
||||
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
cameraIdx = GetCameraIdx();
|
||||
|
||||
if (CameraCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
UiBridge.Bridge.OnCameraInfo(this.cameraIdx, string.Format("{0} s/n {1} si simulated", Name, CameraCfg.HardwareAddress));
|
||||
return;
|
||||
}
|
||||
|
||||
/// Detect a camera
|
||||
bool cameraDetected = DetectCamera(CameraCfg.HardwareAddress % 100, ref ipAddress, true,
|
||||
out cpuHardware, out cpuRevision, out cpuSerialNr, out sdCardVer);
|
||||
|
||||
if (CameraCfg.DebugLevel == DebugMode.AutoDetect)
|
||||
{
|
||||
CameraCfg.DebugLevel = cameraDetected ? DebugMode.DetectedOn : DebugMode.DetectedOff;
|
||||
}
|
||||
else if (!cameraDetected)
|
||||
{
|
||||
throw new Exception(string.Format("{0}={1}", Strings.Address, CameraCfg.HardwareAddress));
|
||||
}
|
||||
|
||||
if (cameraDetected)
|
||||
{
|
||||
UiBridge.Bridge.OnCameraInfo(this.cameraIdx, string.Format("{0} ver.{1} {2} s/n {3}", Name, sdCardVer, ipAddress, CameraCfg.HardwareAddress));
|
||||
|
||||
ClpUserName = (sdCardVer.Length > 0 && sdCardVer[0] == '1') ? "tbf" : "pi";
|
||||
|
||||
rtpUdpLocalPort = UdpPortBase + 4 * cameraIdx;
|
||||
rtcpUdpLocalPort = UdpPortBase + 4 * cameraIdx + 1;
|
||||
msrmntUdpLocalPort = UdpPortBase + 4 * cameraIdx + 2;
|
||||
|
||||
StartRtpListener(NetAdapter.IPAddress, rtpUdpLocalPort);
|
||||
StartMsrmntListener(NetAdapter.IPAddress, msrmntUdpLocalPort);
|
||||
|
||||
connectionInfo = new PasswordConnectionInfo(IPAddress.ToString(), ClpUserName, ClpPassword);
|
||||
|
||||
/// Open telnet clint and start the log-in process
|
||||
Telnet = new Telnet.TelnetClient(this, CameraCfg.Name, ClpUserName, ClpPassword, TelnetPrompt, TelnetSkipFirstLine, CameraCfg.DisplayTerminal);
|
||||
|
||||
Telnet.promptReceivedHandler += delegate(object sndr, PromptReceivedEventArgs a)
|
||||
{
|
||||
OnPromptReceived(sndr, a);
|
||||
};
|
||||
|
||||
if (CameraCfg.DisplayTerminal)
|
||||
{
|
||||
terminalDlg = new TerminalDlg(CameraCfg.Name, Telnet);
|
||||
terminalDlg.Show();
|
||||
}
|
||||
|
||||
Telnet.Enqueue(new Telnet.Command(Network.Telnet.CmdAction.CONNECT, ipAddress.ToString(), 10, 30));
|
||||
Telnet.Enqueue(new Telnet.Command(Network.Telnet.CmdAction.CLEAR_RESPONSE));
|
||||
Telnet.Enqueue(new Telnet.Command(Network.Telnet.CmdAction.SEND_COMMAND, "sha1sum clp/*"));
|
||||
|
||||
log.FatalFormat("'{0}' with address {1} detected, IP = {2}, SD = {3}", Name, CameraCfg.HardwareAddress, ipAddress, sdCardVer);
|
||||
}
|
||||
else
|
||||
{
|
||||
UiBridge.Bridge.OnCameraInfo(this.cameraIdx, string.Format("{0} s/n {1} was not detected", Name, CameraCfg.HardwareAddress));
|
||||
log.FatalFormat("'{0}' with address {1} not detected", Name, CameraCfg.HardwareAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopDevice()
|
||||
{
|
||||
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
||||
{
|
||||
running = false;
|
||||
|
||||
stopScpThreadFlag = true;
|
||||
stopRtpListenerFlag = true;
|
||||
stopMsrmntListenerFlag = true;
|
||||
|
||||
if (Telnet != null)
|
||||
{
|
||||
Telnet.Dispose();
|
||||
Telnet = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StopDevice2()
|
||||
{
|
||||
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
||||
{
|
||||
if (scpThread != null) scpThread.Join(50);
|
||||
if (rtpListenerThread != null) rtpListenerThread.Join(50);
|
||||
if (msrmntListenerThread != null) msrmntListenerThread.Join(50);
|
||||
|
||||
if (rtpUdpClient != null)
|
||||
{
|
||||
rtpUdpClient.Close();
|
||||
rtpUdpClient = null;
|
||||
}
|
||||
|
||||
if (msrmntUdpClient != null)
|
||||
{
|
||||
msrmntUdpClient.Close();
|
||||
msrmntUdpClient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RunDeviceBefore()
|
||||
{
|
||||
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
||||
{
|
||||
if (sha1sumResponse != null && !sha1sumResponseProcessed)
|
||||
{
|
||||
scpThread = new Thread(new ThreadStart(ScpWorker));
|
||||
scpThread.Start();
|
||||
|
||||
sha1sumResponseProcessed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void RunDeviceAfter() { }
|
||||
|
||||
|
||||
void OnPromptReceived(object sndr, PromptReceivedEventArgs a)
|
||||
{
|
||||
if (sha1sumResponse == null)
|
||||
{
|
||||
sha1sumResponse = a.Response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Configuration Change Handling
|
||||
|
||||
public static void OnCfgChange(object sender, CfgChangeArgs args)
|
||||
{
|
||||
if (CfgChangeHandler == null) return;
|
||||
try { CfgChangeHandler(sender, args); }
|
||||
catch (Exception e) { log.Error("CfgChangeHandler(...) failed", e); }
|
||||
}
|
||||
|
||||
public static event EventHandler<CfgChangeArgs> CfgChangeHandler;
|
||||
|
||||
public override void StartChangeHandler()
|
||||
{
|
||||
CfgChangeHandler += delegate(object sender, CfgChangeArgs args)
|
||||
{
|
||||
CameraCfg tmpcfg = args.Cfg as CameraCfg;
|
||||
if (tmpcfg != null && tmpcfg.Name.Equals(Name))
|
||||
{
|
||||
if (args.Command == CfgChangeCmd.CfgChange)
|
||||
{
|
||||
CameraCfg.TestImagesMode = tmpcfg.TestImagesMode;
|
||||
CameraCfg.TestImagesCount = tmpcfg.TestImagesCount;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Configuration Change Handling
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Detect the camera with specified hardware address (DIP switches).
|
||||
/// Optionally set camera date and time.
|
||||
/// </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="sdCardVer">Detected OS image</param>
|
||||
/// <returns>true when camera detected successfully</returns>
|
||||
bool DetectCamera(int hwAddress, ref IPAddress ipAddress, bool setDateTime, out string hardware, out string revision, out string serial, out string sdCardVer)
|
||||
{
|
||||
for (int trials = 1; trials <= 3; trials++)
|
||||
{
|
||||
UdpClient udpClient = new UdpClient();
|
||||
|
||||
try
|
||||
{
|
||||
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];
|
||||
sdCardVer = 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];
|
||||
sdCardVer = 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.Debug(msg);
|
||||
Console.WriteLine(msg);
|
||||
|
||||
return true; /// Camera detected
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.ErrorFormat("Camera {0} with HWAddress={1} not detected : {2}", CameraCfg.Name, CameraCfg.HardwareAddress, exc.Message);
|
||||
}
|
||||
|
||||
udpClient.Close();
|
||||
}
|
||||
|
||||
ipAddress = null;
|
||||
hardware = null;
|
||||
revision = null;
|
||||
serial = null;
|
||||
sdCardVer = 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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ScpWorker()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var scpClient = new ScpClient(connectionInfo))
|
||||
{
|
||||
scpClient.Connect();
|
||||
|
||||
DirectoryInfo dirInfo = null;
|
||||
if (sdCardVer.Length > 0)
|
||||
{
|
||||
switch (sdCardVer[0])
|
||||
{
|
||||
case '1':
|
||||
dirInfo = new DirectoryInfo(Path.Combine(System.Windows.Forms.Application.StartupPath, "CameraBinaryFiles"));
|
||||
break;
|
||||
case '2':
|
||||
dirInfo = new DirectoryInfo(Path.Combine(System.Windows.Forms.Application.StartupPath, "CameraBinaryFiles2"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirInfo != null)
|
||||
{
|
||||
///
|
||||
/// Synchronize files in camera /home/tbf/clp directory with the files in "TBF/CameraBinaryFiles" project folder
|
||||
///
|
||||
using (var sshClient = new SshClient(connectionInfo))
|
||||
{
|
||||
sshClient.Connect();
|
||||
|
||||
SHA1 sha1calculator = SHA1.Create();
|
||||
StringBuilder sha1str = new StringBuilder();
|
||||
|
||||
FileInfo[] fileInfos = dirInfo.GetFiles();
|
||||
foreach (FileInfo fi in fileInfos)
|
||||
{
|
||||
lock (fileUploadLock)
|
||||
{
|
||||
FileStream fs = fi.Open(FileMode.Open);
|
||||
fs.Position = 0;
|
||||
byte[] sha1 = sha1calculator.ComputeHash(fs);
|
||||
fs.Close();
|
||||
|
||||
sha1str.Clear();
|
||||
foreach (var b in sha1) sha1str.Append(b.ToString("x2"));
|
||||
|
||||
if (!sha1sumResponse.Contains(string.Format("{0} clp/{1}", sha1str, fi.Name)))
|
||||
{
|
||||
/// Copy a file to the camera if SHA1 sum is different of the file is missing
|
||||
log.InfoFormat("{0}, IP={1}, Uploading file {2}", Name, IPAddress, fi.Name);
|
||||
scpClient.Upload(fi, string.Format("~/clp/{0}", fi.Name));
|
||||
}
|
||||
}
|
||||
|
||||
if (!sha1sumResponse.Contains(fi.Name))
|
||||
{
|
||||
/// Change file properties if it was a new file
|
||||
string command = string.Format("chmod 755 ~/clp/{0}", fi.Name);
|
||||
log.InfoFormat("{0}, IP={1}, Executing command '{2}'", Name, IPAddress, command);
|
||||
sshClient.CreateCommand(command).Execute();
|
||||
|
||||
//if (fi.Name.Equals("runmeonce"))
|
||||
//{
|
||||
// log.InfoFormat("{0}, IP={1}, Executing command 'sudo clp/runmeonce'", Name, IPAddress);
|
||||
// sshClient.CreateCommand("sudo clp/runmeonce").Execute();
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scpConnected = scpClient.IsConnected;
|
||||
running = true;
|
||||
|
||||
///
|
||||
/// Run SCP command loop
|
||||
///
|
||||
while (scpConnected && !stopScpThreadFlag)
|
||||
{
|
||||
switch (scpCommand)
|
||||
{
|
||||
case ScpCommand.Get:
|
||||
scpCommand = ScpCommand.None;
|
||||
scpClient.Download(scpRemoteFileName, new System.IO.FileInfo(scpLocalFileName));
|
||||
break;
|
||||
|
||||
case ScpCommand.Put:
|
||||
scpCommand = ScpCommand.None;
|
||||
scpClient.Upload(new System.IO.FileInfo(scpLocalFileName), scpRemoteFileName);
|
||||
break;
|
||||
|
||||
default:
|
||||
Thread.Sleep(250);
|
||||
break;
|
||||
}
|
||||
|
||||
scpConnected = scpClient.IsConnected;
|
||||
}
|
||||
|
||||
running = false;
|
||||
scpClient.Disconnect();
|
||||
scpConnected = false;
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.ErrorFormat( "Exception in {0} ScpWorker(): {1}", Name, exc.Message);
|
||||
if (exc.InnerException != null)
|
||||
{
|
||||
log.ErrorFormat(" Inner exception: {0}", exc.InnerException.Message);
|
||||
}
|
||||
|
||||
scpConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download a file from the camera via SCP
|
||||
/// </summary>
|
||||
/// <param name="srcFileName"></param>
|
||||
/// <returns>0 (success), -1 (busy), -2 (no session)</returns>
|
||||
public int DownloadFile(string srcFileName, string dstFileName)
|
||||
{
|
||||
if (!scpConnected) return -2;
|
||||
if (scpCommand != ScpCommand.None) return -1;
|
||||
|
||||
scpRemoteFileName = srcFileName;
|
||||
scpLocalFileName = dstFileName;
|
||||
scpCommand = ScpCommand.Get;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload a file to the camera via SCP
|
||||
/// </summary>
|
||||
/// <param name="srcFileName"></param>
|
||||
/// <returns>0 (success), -1 (busy), -2 (no session)</returns>
|
||||
public int UploadFile(string srcFileName, string dstFileName)
|
||||
{
|
||||
if (!scpConnected) return -2;
|
||||
if (scpCommand != ScpCommand.None) return -1;
|
||||
|
||||
scpLocalFileName = srcFileName;
|
||||
scpRemoteFileName = dstFileName;
|
||||
scpCommand = ScpCommand.Put;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
private void StartRtpListener(IPAddress localIP, int localPort)
|
||||
{
|
||||
rtpUdpClient = new UdpClient(new IPEndPoint(localIP, localPort));
|
||||
rtpListenerThread = new Thread(new ThreadStart(RtpListener));
|
||||
rtpListenerThread.Start();
|
||||
}
|
||||
|
||||
void RtpListener()
|
||||
{
|
||||
JpegFrame jpegFrame = new JpegFrame();
|
||||
bool synced = false;
|
||||
|
||||
while (!stopRtpListenerFlag)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint endpoint = null;
|
||||
Byte[] data = rtpUdpClient.Receive(ref endpoint);
|
||||
|
||||
if (data.Length > 20)
|
||||
{
|
||||
/// Process the packet
|
||||
RtpPacket newPacket = new RtpPacket(data);
|
||||
if (synced)
|
||||
{
|
||||
/// Sychronized: process the new packet
|
||||
switch (jpegFrame.AddPacket(newPacket))
|
||||
{
|
||||
case RtpFrame.STATE.Complete:
|
||||
UiBridge.Bridge.OnImage(this.cameraIdx, jpegFrame.ToImage());
|
||||
jpegFrame.Clear();
|
||||
break;
|
||||
case RtpFrame.STATE.Error:
|
||||
jpegFrame.Clear();
|
||||
synced = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (newPacket.Mark)
|
||||
{
|
||||
/// Synchronize when a packet with Mark occurs, process the next packet
|
||||
jpegFrame.Clear();
|
||||
synced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Stay unsynchronized
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
string msg = string.Format("Exception in MJpeg RTP listener thread: {0}", exc.Message);
|
||||
log.Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void StartMsrmntListener(IPAddress localIP, int localPort)
|
||||
{
|
||||
msrmntUdpClient = new UdpClient(new IPEndPoint(localIP, localPort));
|
||||
msrmntListenerThread = new Thread(new ThreadStart(MsrmntListener));
|
||||
msrmntListenerThread.Start();
|
||||
}
|
||||
|
||||
void MsrmntListener()
|
||||
{
|
||||
StringBuilder toBeProcessed = new StringBuilder();
|
||||
int[] angles = new int[RoisAndResults.MaxRegisteredRois];
|
||||
|
||||
while (!stopMsrmntListenerFlag)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint endpoint = null;
|
||||
Byte[] data = msrmntUdpClient.Receive(ref endpoint);
|
||||
|
||||
toBeProcessed.Append(Encoding.ASCII.GetString(data, 0, data.Length));
|
||||
log.DebugFormat("MeasurementUdpListener() ... toBeProcessed = {0}", toBeProcessed);
|
||||
|
||||
while (true)
|
||||
{
|
||||
int from = toBeProcessed.ToString().IndexOf('[');
|
||||
if (from < 0) break;
|
||||
|
||||
int len = toBeProcessed.ToString(from + 1, toBeProcessed.Length - from - 1).IndexOf(']');
|
||||
if (len < 0) break;
|
||||
|
||||
string[] pulsesArr = toBeProcessed.ToString(from + 1, len).Split(new char[] { ';' });
|
||||
|
||||
bool fmtError = false;
|
||||
long timeMs;
|
||||
if ((pulsesArr.Length >= 2) &&
|
||||
(pulsesArr.Length <= RoisAndResults.MaxRegisteredRois + 1) &&
|
||||
long.TryParse(pulsesArr[0], out timeMs))
|
||||
{
|
||||
for (int i = 1; i < pulsesArr.Length; i++)
|
||||
{
|
||||
int val;
|
||||
if (int.TryParse(pulsesArr[i], out val))
|
||||
{
|
||||
angles[i - 1] = Math.Abs(val); /// TODO: pozerat CW/CCW a podla toho dat +/-
|
||||
}
|
||||
else
|
||||
{
|
||||
fmtError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fmtError) roisAndResults.SetResults(timeMs, angles);
|
||||
}
|
||||
|
||||
toBeProcessed.Remove(0, from + len + 2);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
string msg = string.Format("Exception in the measurement listener thread: {0}", exc.Message);
|
||||
log.Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//public void OnMeasurementDataReceived(object sender, MeasuredDataEventArgs msrmtData)
|
||||
//{
|
||||
// string[] pulsesArr = msrmtData.MeasuredData.Split(new char[] { ';' });
|
||||
|
||||
// uint uiVal;
|
||||
// if (result != null && result.Length + 1 == pulsesArr.Length && uint.TryParse(pulsesArr[0], out uiVal))
|
||||
// {
|
||||
// cameraTimeMs = (long)uiVal;
|
||||
|
||||
// for (int i = 0; i < result.Length; i++)
|
||||
// {
|
||||
// int val;
|
||||
// if (int.TryParse(pulsesArr[i + 1], out val))
|
||||
// {
|
||||
// result[i] = Math.Abs(val);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
public IOperation LiveStreamOp(bool hiRes)
|
||||
{
|
||||
int wid = hiRes ? 1280 : 640;
|
||||
int hgh = hiRes ? 960 : 480;
|
||||
string command = string.Format("/opt/vc/bin/raspivid -t 0 -cd MJPEG -w {0} -h {1} -fps 30 -b 8000000 -o - | gst-launch-1.0 fdsrc ! \"image/jpeg,framerate=30/1\" ! jpegparse ! rtpjpegpay ! udpsink host={2} port={3}",
|
||||
wid, hgh, NetAdapter.IPAddress, rtpUdpLocalPort);
|
||||
//string command = string.Format("~/userland/build/bin/raspivid -t 0 -cd MJPEG -w {0} -h {1} -fps 30 -b 8000000 -o - | gst-launch-1.0 fdsrc ! \"image/jpeg,framerate=30/1\" ! jpegparse ! rtpjpegpay ! udpsink host={2} port={3}",
|
||||
// wid, hgh, NetAdapter.IPAddress, rtpUdpLocalPort);
|
||||
|
||||
return new LiveStreamOp(this, Telnet, command);
|
||||
}
|
||||
|
||||
|
||||
public IOperation GrabImagesOp(string[] imgFileNames, bool blackAndWhite, bool lowResolution, ImageRotation imageRotation)
|
||||
{
|
||||
return new GrabImagesOp(this, imgFileNames, blackAndWhite, lowResolution, imageRotation);
|
||||
}
|
||||
|
||||
|
||||
public IOperation MeasurementOp()
|
||||
{
|
||||
if (Telnet != null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string command;
|
||||
bool measurementCommandSent;
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
/// Prepare the telent command
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i <= roisAndResults.RegisteredRoisCount; i++)
|
||||
{
|
||||
sb.Append(" ");
|
||||
sb.Append(roisAndResults.GetRoiParams(i));
|
||||
}
|
||||
|
||||
int logImagesArg;
|
||||
switch (CameraCfg.TestImagesMode)
|
||||
{
|
||||
default:
|
||||
case TestImagesMode.None:
|
||||
case TestImagesMode.LoadImages:
|
||||
logImagesArg = 0;
|
||||
break;
|
||||
case TestImagesMode.SaveFirstImages:
|
||||
logImagesArg = CameraCfg.TestImagesCount;
|
||||
break;
|
||||
case TestImagesMode.SaveLastImages:
|
||||
logImagesArg = -CameraCfg.TestImagesCount;
|
||||
break;
|
||||
}
|
||||
|
||||
command = string.Format("clp/Measurement -udp {0} {1}{2}{3} 1 7 0 0 {4}",
|
||||
NetAdapter.IPAddress,
|
||||
msrmntUdpLocalPort,
|
||||
(CameraCfg.TestImagesMode == TestImagesMode.LoadImages) ? (" -l " + CameraCfg.TestImagesCount.ToString()) : string.Empty,
|
||||
sb,
|
||||
logImagesArg);
|
||||
|
||||
measurementCommandSent = false;
|
||||
|
||||
/// If ready send (=enqueue) the command
|
||||
if (Telnet.State == TelnetClient.TelnetState.Inactive)
|
||||
{
|
||||
log.WarnFormat("{0} SEND_MSRMNT_CMD: {1}", Name, command);
|
||||
Telnet.Enqueue(new Telnet.Command(CmdAction.SEND_MSRMNT_CMD, command));
|
||||
measurementCommandSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Event Run()
|
||||
{
|
||||
if (!measurementCommandSent)
|
||||
{
|
||||
/// When ready send (=enqueue) the command
|
||||
if (Telnet.State == TelnetClient.TelnetState.Inactive)
|
||||
{
|
||||
log.WarnFormat("{0} SEND_MSRMNT_CMD: {1}", Name, command);
|
||||
Telnet.Enqueue(new Telnet.Command(CmdAction.SEND_MSRMNT_CMD, command));
|
||||
measurementCommandSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Event.None;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (measurementCommandSent)
|
||||
{
|
||||
log.WarnFormat("{0} SEND_COMMAND: CTRL-Q", Name);
|
||||
Telnet.Enqueue(new Telnet.Command(CmdAction.SEND_COMMAND, TelnetClient.Q_Command));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Xml.Serialization;
|
||||
using Config.Entities;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Resources;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public enum TestImagesMode
|
||||
{
|
||||
None,
|
||||
SaveFirstImages,
|
||||
SaveLastImages,
|
||||
LoadImages,
|
||||
Count,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
public class CameraCfg : ComponentCfgBase, IChildComponentCfg
|
||||
{
|
||||
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(CameraCfg) })[0];
|
||||
public override XmlSerializer GetSerializer() { return Serializer; }
|
||||
|
||||
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new CameraCfgCtrl(); }
|
||||
|
||||
///
|
||||
/// Serialized parameters
|
||||
///
|
||||
public int HardwareAddress;
|
||||
public bool DisplayTerminal;
|
||||
|
||||
[XmlIgnore]
|
||||
public TestImagesMode TestImagesMode;
|
||||
|
||||
[XmlIgnore]
|
||||
public int TestImagesCount;
|
||||
|
||||
|
||||
/// 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;
|
||||
TestImagesMode = TestImagesMode.None;
|
||||
TestImagesCount = 3000;
|
||||
DebugLevel = Config.Entities.DebugMode.AutoDetect;
|
||||
}
|
||||
|
||||
public string ToString(int i)
|
||||
{
|
||||
return string.Format("Name={0}, s/n={1}, Terminal={2}, TestImagesMode={3}, TestImagesCount={4}, Parent={5}",
|
||||
Name,
|
||||
HardwareAddress,
|
||||
DisplayTerminal ? Strings.yes : Strings.no,
|
||||
TestImagesMode,
|
||||
TestImagesCount,
|
||||
ParentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using Config.Entities;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Resources;
|
||||
using TBF.UI.Bench.Components;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public partial class CameraCfgCtrl : UserControl, IComponentCfgCtrl
|
||||
{
|
||||
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)
|
||||
{
|
||||
Localize();
|
||||
|
||||
parent = ParentForm as ComponentParametersDlg;
|
||||
if (parent == null) return;
|
||||
|
||||
if (parent.CmpntEntities != null)
|
||||
{
|
||||
foreach (var cmpnt in parent.CmpntEntities)
|
||||
{
|
||||
if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.Rig.Network.Adapter.Factory)
|
||||
{
|
||||
parentNameComboBox.Items.Add(cmpnt.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TestImagesMode mode = 0; mode < TestImagesMode.Count; mode++)
|
||||
{
|
||||
testImagesModeComboBox.Items.Add(mode.ToString());
|
||||
}
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void Localize()
|
||||
{
|
||||
nameLabel.Text = Strings.Name;
|
||||
parentNameLabel.Text = Strings.Parent_name;
|
||||
hwAddressLabel.Text = Strings.Serial_Number;
|
||||
}
|
||||
|
||||
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;
|
||||
testImagesModeComboBox.Text = config.TestImagesMode.ToString();
|
||||
testImagesCountTextBox.Text = config.TestImagesCount.ToString();
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
nameTextBox.Enabled = true;
|
||||
parentNameComboBox.Enabled = true;
|
||||
hwAddressTextBox.Enabled = true;
|
||||
displayTerminalCheckBox.Enabled = true;
|
||||
testImagesModeComboBox.Enabled = true;
|
||||
testImagesCountTextBox.Enabled = true;
|
||||
}
|
||||
|
||||
public CfgUpdateFlags VerifyCfg(ref string message)
|
||||
{
|
||||
CfgUpdateFlags flags = CfgUpdateFlags.None;
|
||||
|
||||
int dummy;
|
||||
if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
|
||||
{
|
||||
flags |= CfgUpdateFlags.Error;
|
||||
message += Environment.NewLine + string.Format(Strings.Invalid_0, parentNameLabel.Text);
|
||||
}
|
||||
|
||||
if (!int.TryParse(hwAddressTextBox.Text, out dummy) || dummy % 100 > 63)
|
||||
{
|
||||
flags |= CfgUpdateFlags.Error;
|
||||
message += Environment.NewLine + string.Format(Strings.Invalid_0, hwAddressLabel.Text);
|
||||
}
|
||||
|
||||
TestImagesMode newTestImagesMode = TestImagesMode.Invalid;
|
||||
for (TestImagesMode mode = 0; mode < TestImagesMode.Count; mode++)
|
||||
{
|
||||
if (testImagesModeComboBox.Text == mode.ToString()) newTestImagesMode = mode;
|
||||
}
|
||||
if (newTestImagesMode == TestImagesMode.Invalid)
|
||||
{
|
||||
flags |= CfgUpdateFlags.Error;
|
||||
message += Environment.NewLine + string.Format(Strings.Invalid_0, testImagesModeLabel.Text);
|
||||
}
|
||||
|
||||
if (!int.TryParse(testImagesCountTextBox.Text, out dummy) || dummy < 0 || dummy > 3000
|
||||
|| ((dummy == 0) && (newTestImagesMode != TestImagesMode.None)))
|
||||
{
|
||||
flags |= CfgUpdateFlags.Error;
|
||||
message += Environment.NewLine + string.Format(Strings.Invalid_0, testImagesCountLabel.Text);
|
||||
}
|
||||
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public CfgUpdateFlags UpdateCfg()
|
||||
{
|
||||
CfgUpdateFlags flags = CfgUpdateFlags.None;
|
||||
|
||||
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
|
||||
|
||||
string newParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
|
||||
int newHWAddress = int.Parse(hwAddressTextBox.Text);
|
||||
TestImagesMode newTestImagesMode = 0;
|
||||
for (TestImagesMode mode = 0; mode < TestImagesMode.Count; mode++)
|
||||
{
|
||||
if (testImagesModeComboBox.Text == mode.ToString()) newTestImagesMode = mode;
|
||||
}
|
||||
int newTestImagesCount = int.Parse(testImagesCountTextBox.Text);
|
||||
|
||||
|
||||
if (config.Name != nameTextBox.Text ||
|
||||
config.ParentName != newParentName ||
|
||||
config.HardwareAddress != newHWAddress ||
|
||||
config.DisplayTerminal != displayTerminalCheckBox.Checked)
|
||||
{
|
||||
config.Name = nameTextBox.Text;
|
||||
config.ParentName = newParentName;
|
||||
config.HardwareAddress = newHWAddress;
|
||||
config.DisplayTerminal = displayTerminalCheckBox.Checked;
|
||||
|
||||
flags |= (CfgUpdateFlags.RestartRqrd | CfgUpdateFlags.AnyChange);
|
||||
}
|
||||
|
||||
|
||||
if (config.TestImagesMode != newTestImagesMode ||
|
||||
config.TestImagesCount != newTestImagesCount)
|
||||
{
|
||||
config.TestImagesMode = newTestImagesMode;
|
||||
config.TestImagesCount = newTestImagesCount;
|
||||
|
||||
flags |= (CfgUpdateFlags.InvokeCfgChange | CfgUpdateFlags.VolatileChange);
|
||||
}
|
||||
|
||||
if ((flags & CfgUpdateFlags.InvokeCfgChange) != 0)
|
||||
{
|
||||
Camera.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.CfgChange, config));
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
namespace TBF.Rig.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.hwAddressLabel = 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.testImagesCountTextBox = new System.Windows.Forms.TextBox();
|
||||
this.testImagesCountLabel = new System.Windows.Forms.Label();
|
||||
this.testImagesModeComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.testImagesModeLabel = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// hwAddressTextBox
|
||||
//
|
||||
this.hwAddressTextBox.Enabled = false;
|
||||
this.hwAddressTextBox.Location = new System.Drawing.Point(132, 92);
|
||||
this.hwAddressTextBox.Name = "hwAddressTextBox";
|
||||
this.hwAddressTextBox.Size = new System.Drawing.Size(130, 20);
|
||||
this.hwAddressTextBox.TabIndex = 6;
|
||||
//
|
||||
// hwAddressLabel
|
||||
//
|
||||
this.hwAddressLabel.AutoSize = true;
|
||||
this.hwAddressLabel.Location = new System.Drawing.Point(28, 95);
|
||||
this.hwAddressLabel.Name = "hwAddressLabel";
|
||||
this.hwAddressLabel.Size = new System.Drawing.Size(71, 13);
|
||||
this.hwAddressLabel.TabIndex = 5;
|
||||
this.hwAddressLabel.Text = "Serial number";
|
||||
//
|
||||
// parentNameLabel
|
||||
//
|
||||
this.parentNameLabel.AutoSize = true;
|
||||
this.parentNameLabel.Location = new System.Drawing.Point(28, 69);
|
||||
this.parentNameLabel.Name = "parentNameLabel";
|
||||
this.parentNameLabel.Size = new System.Drawing.Size(67, 13);
|
||||
this.parentNameLabel.TabIndex = 3;
|
||||
this.parentNameLabel.Text = "Parent name";
|
||||
//
|
||||
// nameTextBox
|
||||
//
|
||||
this.nameTextBox.Enabled = false;
|
||||
this.nameTextBox.Location = new System.Drawing.Point(132, 41);
|
||||
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(28, 44);
|
||||
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(129, 17);
|
||||
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(132, 66);
|
||||
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(133, 122);
|
||||
this.displayTerminalCheckBox.Name = "displayTerminalCheckBox";
|
||||
this.displayTerminalCheckBox.Size = new System.Drawing.Size(99, 17);
|
||||
this.displayTerminalCheckBox.TabIndex = 7;
|
||||
this.displayTerminalCheckBox.Text = "Display terminal";
|
||||
this.displayTerminalCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// testImagesCountTextBox
|
||||
//
|
||||
this.testImagesCountTextBox.Enabled = false;
|
||||
this.testImagesCountTextBox.Location = new System.Drawing.Point(132, 174);
|
||||
this.testImagesCountTextBox.Name = "testImagesCountTextBox";
|
||||
this.testImagesCountTextBox.Size = new System.Drawing.Size(46, 20);
|
||||
this.testImagesCountTextBox.TabIndex = 12;
|
||||
//
|
||||
// testImagesCountLabel
|
||||
//
|
||||
this.testImagesCountLabel.AutoSize = true;
|
||||
this.testImagesCountLabel.Location = new System.Drawing.Point(28, 177);
|
||||
this.testImagesCountLabel.Name = "testImagesCountLabel";
|
||||
this.testImagesCountLabel.Size = new System.Drawing.Size(94, 13);
|
||||
this.testImagesCountLabel.TabIndex = 11;
|
||||
this.testImagesCountLabel.Text = "Test images count";
|
||||
//
|
||||
// testImagesModeComboBox
|
||||
//
|
||||
this.testImagesModeComboBox.Enabled = false;
|
||||
this.testImagesModeComboBox.FormattingEnabled = true;
|
||||
this.testImagesModeComboBox.Location = new System.Drawing.Point(132, 148);
|
||||
this.testImagesModeComboBox.Name = "testImagesModeComboBox";
|
||||
this.testImagesModeComboBox.Size = new System.Drawing.Size(130, 21);
|
||||
this.testImagesModeComboBox.TabIndex = 14;
|
||||
//
|
||||
// testImagesModeLabel
|
||||
//
|
||||
this.testImagesModeLabel.AutoSize = true;
|
||||
this.testImagesModeLabel.Location = new System.Drawing.Point(28, 151);
|
||||
this.testImagesModeLabel.Name = "testImagesModeLabel";
|
||||
this.testImagesModeLabel.Size = new System.Drawing.Size(93, 13);
|
||||
this.testImagesModeLabel.TabIndex = 13;
|
||||
this.testImagesModeLabel.Text = "Test images mode";
|
||||
//
|
||||
// CameraCfgCtrl
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.testImagesModeComboBox);
|
||||
this.Controls.Add(this.testImagesModeLabel);
|
||||
this.Controls.Add(this.hwAddressTextBox);
|
||||
this.Controls.Add(this.hwAddressLabel);
|
||||
this.Controls.Add(this.testImagesCountTextBox);
|
||||
this.Controls.Add(this.testImagesCountLabel);
|
||||
this.Controls.Add(this.displayTerminalCheckBox);
|
||||
this.Controls.Add(this.parentNameComboBox);
|
||||
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(402, 254);
|
||||
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 hwAddressLabel;
|
||||
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;
|
||||
private System.Windows.Forms.TextBox testImagesCountTextBox;
|
||||
private System.Windows.Forms.Label testImagesCountLabel;
|
||||
private System.Windows.Forms.ComboBox testImagesModeComboBox;
|
||||
private System.Windows.Forms.Label testImagesModeLabel;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1,27 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.Generic;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public class Factory : IComponentFactory
|
||||
{
|
||||
public string ClassName { get { return this.GetType().Namespace.Substring(8); } }
|
||||
public override string ToString() { return ClassName; }
|
||||
|
||||
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(8), this); }
|
||||
|
||||
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
|
||||
{
|
||||
return ComponentCfgBase.CreateFromDbEntity(CameraCfg.Serializer, component, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using log4net;
|
||||
using Config.Entities;
|
||||
using TBF.Rig.Network.Telnet;
|
||||
using System.IO;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public class GrabImagesOp : IOperation
|
||||
{
|
||||
/// TODO: Implement support for grabbing multiple images, now imgFileNames.Lenght should be 1
|
||||
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(GrabImagesOp));
|
||||
public override string ToString() { return string.Format("GrabImageOp()"); }
|
||||
|
||||
readonly Camera camera;
|
||||
readonly Telnet.TelnetClient telnet;
|
||||
readonly string[] imgFileNames;
|
||||
readonly bool blackAndWhite;
|
||||
readonly bool lowResolution;
|
||||
readonly ImageRotation imageRotation;
|
||||
|
||||
string command;
|
||||
bool grabImageCommandSent;
|
||||
string response;
|
||||
bool grabPassed;
|
||||
bool grabFailed;
|
||||
bool transferringGrabbedImage;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Events: Event.None or Event.Error
|
||||
/// </summary>
|
||||
/// <param name="camera">CLP1611.Camera reference</param>
|
||||
public GrabImagesOp(Camera camera, string[] imgFileNames, bool blackAndWhite, bool lowResolution, ImageRotation imageRotation)
|
||||
{
|
||||
/// TODO: Implement support for grabbing multiple images, now imgFileNames.Lenght should be 1
|
||||
|
||||
this.camera = camera;
|
||||
this.telnet = camera.Telnet;
|
||||
this.imgFileNames = imgFileNames;
|
||||
this.blackAndWhite = blackAndWhite;
|
||||
this.lowResolution = lowResolution;
|
||||
this.imageRotation = imageRotation;
|
||||
|
||||
grabImageCommandSent = false;
|
||||
transferringGrabbedImage = false;
|
||||
|
||||
if (camera.DebugLevel == DebugMode.Normal || camera.DebugLevel == DebugMode.DetectedOn)
|
||||
{
|
||||
telnet.promptReceivedHandler += delegate(object sndr, PromptReceivedEventArgs a)
|
||||
{
|
||||
OnPromptReceived(sndr, a);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void OnPromptReceived(object sndr, PromptReceivedEventArgs a)
|
||||
{
|
||||
response = a.Response;
|
||||
/// TODO
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
grabImageCommandSent = false;
|
||||
transferringGrabbedImage = false;
|
||||
|
||||
if ((imgFileNames != null) && (imgFileNames.Length > 0) && File.Exists(imgFileNames[0]))
|
||||
{
|
||||
///
|
||||
/// An image specified, (1) delete previous image, (2) check if this is a simulation
|
||||
///
|
||||
File.Delete(imgFileNames[0]);
|
||||
|
||||
if (camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.Simulate || camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.DetectedOff)
|
||||
{
|
||||
///
|
||||
/// Camera is in simulation mode => create a simulated image
|
||||
///
|
||||
File.Copy(string.Format("{0}\\Pictures\\sample.jpg", Program.ExecutableDir), imgFileNames[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Event Run()
|
||||
{
|
||||
if ((imgFileNames == null) || (imgFileNames.Length < 1) || (imgFileNames[0] == null))
|
||||
{
|
||||
///
|
||||
/// No images to be grabbed => Done
|
||||
///
|
||||
return Event.GrabPassed;
|
||||
}
|
||||
else if (camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.Simulate ||
|
||||
camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.DetectedOff)
|
||||
{
|
||||
///
|
||||
/// Camera is in simulation mode => create a simulated image and complete
|
||||
///
|
||||
return Event.GrabPassed;
|
||||
}
|
||||
else if (!grabImageCommandSent)
|
||||
{
|
||||
///
|
||||
/// Normal operation, no command sent yet => (1) wait until telnet state = Inactive, (2) send a command
|
||||
///
|
||||
if (camera.Running && (telnet.State == TelnetClient.TelnetState.Inactive))
|
||||
{
|
||||
///
|
||||
/// State variables
|
||||
///
|
||||
response = null;
|
||||
|
||||
///
|
||||
/// Prepare a command
|
||||
///
|
||||
int rotation = 0;
|
||||
if (imageRotation == ImageRotation.Deg90)
|
||||
rotation = 90;
|
||||
else if (imageRotation == ImageRotation.Deg180)
|
||||
rotation = 180;
|
||||
else if (imageRotation == ImageRotation.Deg270)
|
||||
rotation = 270;
|
||||
|
||||
command = string.Format("clp/GrabImage {0} {1} -r {2} {3} -n 1 {4}",
|
||||
blackAndWhite ? "-bmp" : "-bmp", /// 0 ... file format and quality
|
||||
blackAndWhite ? "-y8" : "-rgb", /// 1 ... B&W / color
|
||||
rotation.ToString(), /// 2 ... roration
|
||||
lowResolution ? "-lr" : "-hr", /// 4 ... resolution
|
||||
"grabbed.bmp"); /// 5 ... filename
|
||||
|
||||
//
|
||||
// Send (or enqueue) command
|
||||
//
|
||||
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.CLEAR_RESPONSE));
|
||||
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_COMMAND, command));
|
||||
grabImageCommandSent = true;
|
||||
log.WarnFormat("{0} IP={1} command={2}", camera.Name, camera.IPAddress, command);
|
||||
}
|
||||
return Event.CameraBusy;
|
||||
}
|
||||
else if (grabPassed)
|
||||
{
|
||||
///
|
||||
/// Normal operation, grab passed => transfer/transferring the image
|
||||
///
|
||||
if (transferringGrabbedImage)
|
||||
{
|
||||
/// Grab passed and image transfer is in progress
|
||||
return Event.GrabPassed;
|
||||
}
|
||||
else if (0 == camera.DownloadFile(blackAndWhite ? "grabbed.jpg" : "grabbed.bmp", imgFileNames[0]))
|
||||
{
|
||||
/// File transfer successfully started
|
||||
transferringGrabbedImage = true;
|
||||
return Event.GrabPassed;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Wait until the previous image transfer completes
|
||||
return Event.CameraBusy;
|
||||
}
|
||||
}
|
||||
else if (grabFailed)
|
||||
{
|
||||
///
|
||||
/// Normal operation, grab failed => there in no image to transfer
|
||||
///
|
||||
return Event.GrabFailed;
|
||||
}
|
||||
else if (response != null)
|
||||
{
|
||||
///
|
||||
/// Normal operation
|
||||
///
|
||||
if (response.Contains("completed"))
|
||||
{
|
||||
grabPassed = true;
|
||||
|
||||
///
|
||||
/// Start the file transfer
|
||||
///
|
||||
if (0 == camera.DownloadFile(blackAndWhite ? "grabbed.jpg" : "grabbed.bmp", imgFileNames[0]))
|
||||
{
|
||||
/// File transfer successfully started
|
||||
transferringGrabbedImage = true;
|
||||
return Event.GrabPassed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Event.CameraBusy; /// File transfer not started yet
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grabFailed = true;
|
||||
return Event.GrabFailed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Event.CameraBusy;
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.Simulate) return;
|
||||
|
||||
if (grabImageCommandSent)
|
||||
{
|
||||
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_COMMAND, Telnet.TelnetClient.CtrlCCommand));
|
||||
log.WarnFormat("{0} IP={1} command=CTRL-C", camera.Name, camera.IPAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using log4net;
|
||||
using TBF.Rig.Network.Telnet;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public class LiveStreamOp : IOperation
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(LiveStreamOp));
|
||||
|
||||
readonly Camera camera;
|
||||
readonly Telnet.TelnetClient telnet;
|
||||
readonly string command;
|
||||
|
||||
bool liveStreamCommandSent;
|
||||
|
||||
/// <summary>
|
||||
/// Events: Event.None or Event.Error
|
||||
/// </summary>
|
||||
/// <param name="camera">CLP1611.Camera reference</param>
|
||||
public LiveStreamOp(Camera camera, Telnet.TelnetClient telnet, string command)
|
||||
{
|
||||
this.camera = camera;
|
||||
this.telnet = telnet;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
liveStreamCommandSent = false;
|
||||
}
|
||||
|
||||
public Event Run()
|
||||
{
|
||||
if (camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.Simulate) return Event.None;
|
||||
|
||||
log.DebugFormat("Run(): telnet.State = {0}, liveStreamCommandSent = {1}", telnet.State, liveStreamCommandSent);
|
||||
|
||||
if (!liveStreamCommandSent)
|
||||
{
|
||||
if (camera.Running && (telnet.State == TelnetClient.TelnetState.Inactive))
|
||||
{
|
||||
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_STRING, command));
|
||||
liveStreamCommandSent = true;
|
||||
log.WarnFormat("{0} IP={1} command={2}", camera.Name, camera.IPAddress, command);
|
||||
}
|
||||
}
|
||||
|
||||
return Event.None;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (camera.CameraCfg.DebugLevel == Config.Entities.DebugMode.Simulate) return;
|
||||
|
||||
log.DebugFormat("Stop(): telnet.State = {0}, liveStreamCommandSent = {1}", telnet.State, liveStreamCommandSent);
|
||||
|
||||
if (liveStreamCommandSent)
|
||||
{
|
||||
telnet.Enqueue(new Telnet.Command(Telnet.CmdAction.SEND_COMMAND, Telnet.TelnetClient.CtrlCCommand));
|
||||
log.WarnFormat("{0} IP={1} command=CTRL-C", camera.Name, camera.IPAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
|
||||
namespace TBF.Rig.Network.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements RFC2435
|
||||
/// </summary>
|
||||
public class JpegFrame : RtpFrame
|
||||
{
|
||||
static readonly ILog log = LogManager.GetLogger(typeof(TBF.UI.MainWnd));
|
||||
|
||||
#region Statics
|
||||
|
||||
static byte[] CreateStartOfInformation()
|
||||
{
|
||||
return new byte[] { 0xff, 0xd8 };
|
||||
}
|
||||
|
||||
static byte[] CreateJFIFHeader(uint type, uint width, uint height, ArraySegment<byte> tables, uint dri)
|
||||
{
|
||||
List<byte> result = new List<byte>();
|
||||
result.AddRange(CreateStartOfInformation());
|
||||
|
||||
result.Add(0xff);
|
||||
result.Add(0xe0);//AppFirst
|
||||
result.Add(0x00);
|
||||
result.Add(0x10);//length
|
||||
result.Add((byte)'J');
|
||||
result.Add((byte)'F');
|
||||
result.Add((byte)'I');
|
||||
result.Add((byte)'F');
|
||||
result.Add(0x00);
|
||||
|
||||
result.Add(0x01);//Version Major
|
||||
result.Add(0x01);//Version Minor
|
||||
|
||||
result.Add(0x00);//Units
|
||||
|
||||
result.Add(0x00);//Horizontal
|
||||
result.Add(0x01);
|
||||
|
||||
result.Add(0x00);//Vertical
|
||||
result.Add(0x01);
|
||||
|
||||
result.Add(0x00);//No thumb
|
||||
result.Add(0x00);
|
||||
|
||||
if (dri > 0)
|
||||
{
|
||||
result.AddRange(CreateDataRestartIntervalMarker(dri));
|
||||
}
|
||||
|
||||
result.AddRange(CreateQuantizationTablesMarker(tables));
|
||||
|
||||
result.Add(0xff);
|
||||
result.Add(0xc0);//SOF
|
||||
result.Add(0x00);
|
||||
result.Add(0x11);
|
||||
result.Add(0x08);
|
||||
result.Add((byte)(height >> 8));
|
||||
result.Add((byte)height);
|
||||
result.Add((byte)(width >> 8));
|
||||
result.Add((byte)width);
|
||||
|
||||
result.Add(0x03);
|
||||
result.Add(0x01);
|
||||
result.Add((byte)(type > 0 ? 0x22 : 0x21));
|
||||
result.Add(0x00);
|
||||
|
||||
result.Add(0x02);
|
||||
result.Add(0x11);
|
||||
result.Add(0x01);
|
||||
|
||||
result.Add(0x03);
|
||||
result.Add(0x11);
|
||||
result.Add(0x01);
|
||||
|
||||
//Huffman Tables
|
||||
result.AddRange(CreateHuffmanTableMarker(lum_dc_codelens, lum_dc_symbols, 0, 0));
|
||||
result.AddRange(CreateHuffmanTableMarker(lum_ac_codelens, lum_ac_symbols, 0, 1));
|
||||
result.AddRange(CreateHuffmanTableMarker(chm_dc_codelens, chm_dc_symbols, 1, 0));
|
||||
result.AddRange(CreateHuffmanTableMarker(chm_ac_codelens, chm_ac_symbols, 1, 1));
|
||||
|
||||
result.Add(0xff);
|
||||
result.Add(0xda);//Marker SOS
|
||||
result.Add(0x00);
|
||||
result.Add(0x0c);
|
||||
result.Add(0x03);
|
||||
result.Add(0x01);
|
||||
result.Add(0x00);
|
||||
result.Add(0x02);
|
||||
result.Add(0x11);
|
||||
result.Add(0x03);
|
||||
result.Add(0x11);
|
||||
result.Add(0x00);
|
||||
result.Add(0x3f);
|
||||
result.Add(0x00);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// The default 'luma' and 'chroma' quantizer tables, in zigzag order:
|
||||
static byte[] defaultQuantizers = new byte[]
|
||||
{
|
||||
// luma table:
|
||||
16, 11, 12, 14, 12, 10, 16, 14,
|
||||
13, 14, 18, 17, 16, 19, 24, 40,
|
||||
26, 24, 22, 22, 24, 49, 35, 37,
|
||||
29, 40, 58, 51, 61, 60, 57, 51,
|
||||
56, 55, 64, 72, 92, 78, 64, 68,
|
||||
87, 69, 55, 56, 80, 109, 81, 87,
|
||||
95, 98, 103, 104, 103, 62, 77, 113,
|
||||
121, 112, 100, 120, 92, 101, 103, 99,
|
||||
// chroma table:
|
||||
17, 18, 18, 24, 21, 24, 47, 26,
|
||||
26, 47, 99, 66, 56, 66, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Luma and Chroma Table using the default quantizer
|
||||
/// </summary>
|
||||
/// <param name="Q">The quality</param>
|
||||
/// <returns>64 luma bytes and 64 chroma</returns>
|
||||
static byte[] CreateQuantizationTables(uint Q)
|
||||
{
|
||||
int factor = (int)Q;
|
||||
int q;
|
||||
|
||||
if (Q < 1) factor = 1;
|
||||
else if (Q > 99) factor = 99;
|
||||
|
||||
if (Q < 50)
|
||||
{
|
||||
q = 5000 / factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
q = 200 - factor * 2;
|
||||
}
|
||||
byte[] resultTables = new byte[128];
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
int newVal = (defaultQuantizers[i] * q + 50) / 100;
|
||||
if (newVal < 1) newVal = 1;
|
||||
else if (newVal > 255) newVal = 255;
|
||||
resultTables[i] = (byte)newVal;
|
||||
}
|
||||
return resultTables;
|
||||
}
|
||||
|
||||
static byte[] CreateQuantizationTablesMarker(ArraySegment<byte> tables)
|
||||
{
|
||||
List<byte> result = new List<byte>();
|
||||
|
||||
int tableSize = tables.Count / 2;
|
||||
|
||||
//Luma
|
||||
|
||||
result.Add(0xff);
|
||||
result.Add(0xdb);
|
||||
|
||||
result.Add(0x00);
|
||||
result.Add((byte)(tableSize + 3));
|
||||
result.Add(0x00);
|
||||
|
||||
for (int i = 0, e = tableSize; i < e; ++i)
|
||||
{
|
||||
result.Add(tables.Array[tables.Offset + i]);
|
||||
}
|
||||
|
||||
//Chroma
|
||||
|
||||
result.Add(0xff);
|
||||
result.Add(0xdb);
|
||||
|
||||
result.Add(0x00);
|
||||
result.Add((byte)(tableSize + 3));
|
||||
result.Add(0x01);
|
||||
|
||||
for (int i = tableSize, e = tables.Count; i < e; ++i)
|
||||
{
|
||||
result.Add(tables.Array[tables.Offset + i]);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
|
||||
}
|
||||
|
||||
static byte[] lum_dc_codelens = { 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
static byte[] lum_dc_symbols = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||
|
||||
static byte[] lum_ac_codelens = { 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d, };
|
||||
|
||||
static byte[] lum_ac_symbols = {
|
||||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa,
|
||||
};
|
||||
|
||||
static byte[] chm_dc_codelens = { 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
|
||||
|
||||
static byte[] chm_dc_symbols = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||
|
||||
static byte[] chm_ac_codelens = { 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
|
||||
|
||||
static byte[] chm_ac_symbols = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa,
|
||||
};
|
||||
|
||||
static byte[] CreateHuffmanTableMarker(byte[] codeLens, byte[] symbols, int tableNo, int tableClass)
|
||||
{
|
||||
List<byte> result = new List<byte>();
|
||||
result.Add(0xff);
|
||||
result.Add(0xc4);
|
||||
result.Add(0x00);
|
||||
result.Add((byte)(3 + codeLens.Length + symbols.Length));
|
||||
result.Add((byte)((tableClass << 4) | tableNo));
|
||||
result.AddRange(codeLens);
|
||||
result.AddRange(symbols);
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
static byte[] CreateDataRestartIntervalMarker(uint dri)
|
||||
{
|
||||
return new byte[] { 0xff, 0xdd, 0x00, 0x04, (byte)(dri >> 8), (byte)(dri) };
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public JpegFrame() : base(26) { }
|
||||
|
||||
/// <summary>
|
||||
/// Writes packets to a memory stream and created the default header and quantization tables if necessary.
|
||||
/// </summary>
|
||||
void ProcessPackets(System.IO.MemoryStream buffer)
|
||||
{
|
||||
ArraySegment<byte> tables = default(ArraySegment<byte>);
|
||||
uint FragmentOffset, TypeSpecific, Type, Quality, Width, Height, DataRestartInterval = 0;
|
||||
|
||||
for (int i = 0, e = this.Packets.Count, last = e - 1; i < e; ++i)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
RtpPacket packet = this.Packets[i];
|
||||
|
||||
TypeSpecific = (uint)(packet.Payload[offset++]);
|
||||
|
||||
//FragmentOffset = (uint)(packet.Payload[offset++] | packet.Payload[offset++] << 8 | packet.Payload[offset++] << 16);
|
||||
FragmentOffset = (uint)(packet.Payload[offset++] << 16 | packet.Payload[offset++] << 8 | packet.Payload[offset++]);
|
||||
|
||||
//Ensure start packet
|
||||
if (i == 0 && FragmentOffset != 0) continue;
|
||||
|
||||
Type = (uint)(packet.Payload[offset++]);
|
||||
Quality = (uint)packet.Payload[offset++];
|
||||
Width = (uint)(packet.Payload[offset++] * 8);
|
||||
Height = (uint)(packet.Payload[offset++] * 8);
|
||||
|
||||
//Only occur in the first packet
|
||||
if (FragmentOffset == 0)
|
||||
{
|
||||
//If There is already a StartOfInformation present just copy
|
||||
if (packet.Payload[offset] == 0xff && packet.Payload[offset + 1] == 0xd8)
|
||||
{
|
||||
goto WritePacket;
|
||||
}
|
||||
|
||||
//Get the restart interval is present
|
||||
if (Type > 63)
|
||||
{
|
||||
DataRestartInterval = (uint)(packet.Payload[offset++] << 8 | packet.Payload[offset++]);
|
||||
}
|
||||
|
||||
//Get the Q Tables if present
|
||||
if (Quality > 127)
|
||||
{
|
||||
uint MBZ = (uint)(packet.Payload[offset++]);//Should only proceed if MBZ > 0?
|
||||
uint Precision = (uint)(packet.Payload[offset++]);//Wasted read
|
||||
uint Length = (uint)(packet.Payload[offset++] << 8 | packet.Payload[offset++]);
|
||||
if(Length > 0)
|
||||
{
|
||||
tables = new ArraySegment<byte>(packet.Payload, offset, (int)Length);
|
||||
offset += (int)Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
tables = new ArraySegment<byte>(CreateQuantizationTables(Quality));
|
||||
}
|
||||
}
|
||||
|
||||
//Write the header to the buffer
|
||||
byte[] header = CreateJFIFHeader(Type, Width, Height, tables, DataRestartInterval);
|
||||
buffer.Write(header, 0, header.Length);
|
||||
|
||||
}
|
||||
|
||||
WritePacket: //Copy rest of data to buffer
|
||||
buffer.Write(packet.Payload, offset, packet.Payload.Length - offset);
|
||||
|
||||
//Check to see if EOI is present or needs to be added on the last packet
|
||||
if (i == last && packet.Payload[packet.Payload.Length - 2] != 0xff && packet.Payload[packet.Payload.Length - 1] != 0xd9)
|
||||
{
|
||||
buffer.WriteByte(0xff);
|
||||
buffer.WriteByte(0xd9);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Go back to the beginning
|
||||
buffer.Seek(0, System.IO.SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a image from the processed packets in the memory stream
|
||||
/// </summary>
|
||||
/// <returns>The image created from the packets</returns>
|
||||
public System.Drawing.Image ToImage()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (System.IO.MemoryStream buffer = new System.IO.MemoryStream())
|
||||
{
|
||||
ProcessPackets(buffer);
|
||||
return System.Drawing.Image.FromStream(buffer, false, true);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.ErrorFormat("Exception in ToImage(): {0}", exc.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
///
|
||||
/// Copyright (c) 2017 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CLP1611
|
||||
{
|
||||
public class RoisAndResults
|
||||
{
|
||||
public const int MaxRegisteredRois = 4; /// Max. count of registered (and measured) ROI-s
|
||||
|
||||
public long CameraTimeMs;
|
||||
public int[] Angles;
|
||||
|
||||
private IList<string> roiParams;
|
||||
public int RegisteredRoisCount { get { return roiParams.Count; } }
|
||||
|
||||
|
||||
public RoisAndResults()
|
||||
{
|
||||
Angles = new int[MaxRegisteredRois];
|
||||
roiParams = new List<string>();
|
||||
}
|
||||
|
||||
|
||||
public void ClearRoiParams()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
roiParams.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int RegisterRoi(string roiParam)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.roiParams.Count >= MaxRegisteredRois) return 0;
|
||||
|
||||
this.roiParams.Add(roiParam);
|
||||
return this.roiParams.Count; /// return a handle 1 .. MaxRegisteredRois
|
||||
}
|
||||
}
|
||||
|
||||
public string GetRoiParams(int roiHandle)
|
||||
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (roiHandle > 0 && roiHandle <= roiParams.Count)
|
||||
{
|
||||
return roiParams[roiHandle - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetResults(long timeMs, int[] angles)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
CameraTimeMs = timeMs;
|
||||
for (int i = 0; i < Math.Min(angles.Length, Angles.Length); i++)
|
||||
{
|
||||
Angles[i] = angles[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetResult(int roiHandle, out long timeMs)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (roiHandle > 0 && roiHandle <= roiParams.Count)
|
||||
{
|
||||
timeMs = CameraTimeMs;
|
||||
return Angles[roiHandle - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
timeMs = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TBF.Rig.Network.Camera
|
||||
{
|
||||
public class RtpFrame
|
||||
{
|
||||
public enum STATE
|
||||
{
|
||||
Empty, /// Initial state after being constructed
|
||||
OK, /// The frame is not empty, but it's not complete yet. It's without any error
|
||||
Error, /// An error occured
|
||||
Complete, /// The frame is complete, consequent AddPacket() calls will be ignored
|
||||
}
|
||||
|
||||
public readonly int PayloadType;
|
||||
public IList<RtpPacket> Packets;
|
||||
public STATE State { get { return state; } }
|
||||
STATE state;
|
||||
|
||||
|
||||
public RtpFrame(int payloadType)
|
||||
{
|
||||
PayloadType = payloadType;
|
||||
Packets = new List<RtpPacket>();
|
||||
state = STATE.Empty;
|
||||
}
|
||||
|
||||
public STATE AddPacket(RtpPacket packet)
|
||||
{
|
||||
if (state == STATE.Error || packet.PayloadType != PayloadType)
|
||||
{
|
||||
return (state = STATE.Error); /// Already in error or wrong payload type
|
||||
}
|
||||
else if (state == STATE.Complete)
|
||||
{
|
||||
return STATE.Complete; /// Already complete
|
||||
}
|
||||
else if (state != STATE.Empty && packet.SequenceNumber != Packets.Last<RtpPacket>().SequenceNumber + 1)
|
||||
{
|
||||
return (state = STATE.Error); /// Packet out of order
|
||||
}
|
||||
|
||||
/// Going to add a new packet previous ones
|
||||
Packets.Add(packet);
|
||||
|
||||
if (packet.Mark)
|
||||
{
|
||||
return (state = STATE.Complete); /// Complete now
|
||||
}
|
||||
else
|
||||
{
|
||||
return (state = STATE.OK); /// OK, not complete yet
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Packets.Clear();
|
||||
state = STATE.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.Network.Camera
|
||||
{
|
||||
public class RtpPacket
|
||||
{
|
||||
public readonly int Version;
|
||||
public readonly bool P;
|
||||
public readonly bool X;
|
||||
public readonly int CC;
|
||||
public readonly bool Mark;
|
||||
public readonly int PayloadType;
|
||||
public readonly int SequenceNumber;
|
||||
public readonly int Timestamp;
|
||||
public readonly int SsrcId;
|
||||
public readonly Byte[] Payload; /// Data without RTP header
|
||||
|
||||
public RtpPacket(Byte[] data)
|
||||
{
|
||||
if (data.Length < 12) throw new Exception("Packet too short");
|
||||
|
||||
Version = (data[0] >> 6) & 0x02; /// 1 RTP Version
|
||||
P = ((data[0] & 0x20) != 0); /// 2 RTP P
|
||||
X = ((data[0] & 0x10) != 0); /// 3 RTP X
|
||||
CC = (data[0] & 0x0F); /// 4 RTP CC
|
||||
Mark = ((data[1] & 0x80) != 0); /// 5 RTP M
|
||||
PayloadType = (data[1] & 0x7F); /// 6 RTP PT
|
||||
SequenceNumber = data[2] * 256 + data[3]; /// 7 RTP Seq#
|
||||
Timestamp = ((data[4] * 256 + data[5]) * 256 + data[6]) * 256 + data[7]; /// 8 RTP Timestamp
|
||||
SsrcId = ((data[8] * 256 + data[9]) * 256 + data[10]) * 256 + data[11]; /// 9 RTP SSRC Id
|
||||
|
||||
int ExtensionHeaderSize = 0;
|
||||
if (X && data.Length >= 16 + 4*CC)
|
||||
{
|
||||
ExtensionHeaderSize = data[14 + 4*CC] * 256 + data[15 + 4*CC] + 4;
|
||||
}
|
||||
|
||||
int payloadDataOffset = 12 + 4 * CC + ExtensionHeaderSize;
|
||||
int payloadDataCount = data.Length - 12 - 4 * CC - ExtensionHeaderSize;
|
||||
|
||||
if (payloadDataCount < 0) throw new Exception("Invalid packet");
|
||||
|
||||
Payload = new byte[payloadDataCount];
|
||||
Array.Copy(data, payloadDataOffset, Payload, 0, payloadDataCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace TBF.Rig.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TBF.Rig.Network.Telnet;
|
||||
|
||||
namespace TBF.Rig.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user