/// /// Copyright (c) 2017 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Timers; using log4net; using Renci.SshNet; using Config.Entities; using TBF.BenchControl.Network.Telnet; using TBF.Resources; namespace TBF.BenchControl.Network.Camera.CLP1611 { /// /// This class implements: (1) Camera device, (2) Measurement operation /// 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}, S/N={2}, IPAddress={3}, Serial={4}, Image={5}", this.GetType().Namespace.Substring(17), CameraCfg.Name, CameraCfg.HardwareAddress, ipAddress == null ? "not detected" : ipAddress.ToString(), string.IsNullOrEmpty(cpuSerialNr) ? "not detected" : cpuSerialNr, string.IsNullOrEmpty(sdCardVer) ? "not detected" : sdCardVer); } const string ClpUserName = "tbf"; const string ClpPassword = "C1ern4V0d4"; /// /// 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; /// /// scp support /// Thread scpThread; PasswordConnectionInfo connectionInfo; ScpClient scpClient; bool scpConnected; bool stopScpThreadFlag; string scpSrcFileName; string scpDstFileName; enum ScpCommand { None, Get, } ScpCommand scpCommand; /// /// MJpeg RTP protocol support /// const int RtpUdpPortBase = 20000; int rtpUdpLocalPort; UdpClient rtpUdpClient; Thread rtpListenerThread; static bool stopRtpListenerFlag; /// /// ROI related parameters /// IList roiParams; long cameraTimeMs; int[] result; /// public void ClearRoiParams() { roiParams.Clear(); } public int RegisterRoi(string rParams) { this.roiParams.Add(rParams); int newHandle = this.roiParams.Count; result = new int[newHandle]; return newHandle; } /// public int GetResult(int roiHandle, out long timeMs) { timeMs = cameraTimeMs; if (roiHandle > 0 && roiHandle <= roiParams.Count) { return result[roiHandle - 1]; } return 0; } /// /// UDP Port number for 'MeasurementOp' /// const int UdpPortRangeStart = 13800; const int UdpPortRangeEnd = 13850; static int nextUdpPortNr = UdpPortRangeStart; public static int GetNextUdpPortNr() { return nextUdpPortNr++; } /// readonly int measurementUdpPortNr; UdpClient measurementListener; public Camera() {} public Camera(Generic.IComponentCfg cfg, IList 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; measurementUdpPortNr = GetNextUdpPortNr(); roiParams = new List(); running = false; log.Debug(this.ToString()); } public void Initialize() { cameraIdx = GetCameraIdx(); if (CameraCfg.DebugLevel == DebugMode.Simulate) 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) { /// Open telnet clint and start the log-in process Telnet = new Telnet.TelnetClient(this, CameraCfg.Name, ClpUserName, ClpPassword, TelnetPrompt, TelnetSkipFirstLine, CameraCfg.DisplayTerminal); if (CameraCfg.DisplayTerminal) { terminalDlg = new TerminalDlg(CameraCfg.Name, Telnet); terminalDlg.Show(); } Telnet.MeasurementDataReceivedHandler += delegate(object sender, MeasuredDataEventArgs msrmtData) { if (Program.MainWnd.InvokeRequired) { Program.MainWnd.Invoke(new EventHandler(OnMeasurementDataReceived), sender, msrmtData); } else { OnMeasurementDataReceived(sender, msrmtData); } }; Telnet.Enqueue(new Telnet.Command(Network.Telnet.CmdAction.CONNECT, ipAddress.ToString(), 10, 30)); //measurementListener = new UdpClient(measurementUdpPortNr); //new Thread(new ThreadStart(MeasurementUdpListener)).Start(); scpThread = new Thread(new ThreadStart(ScpWorker)); scpThread.Start(); rtpUdpLocalPort = RtpUdpPortBase + 2 * cameraIdx; StartRtpListener(NetAdapter.IPAddress, rtpUdpLocalPort); running = true; log.FatalFormat("'{0}' with address {1} detected, IP address = {2}", Name, CameraCfg.HardwareAddress, ipAddress); } else { log.FatalFormat("'{0}' with address {1} not detected", Name, CameraCfg.HardwareAddress); } } public void StopDevice() { stopScpThreadFlag = true; stopRtpListenerFlag = true; if (Telnet != null) { Telnet.Dispose(); Telnet = null; } if (scpThread != null) scpThread.Join(500); if (rtpListenerThread != null) rtpListenerThread.Join(1000); if (rtpUdpClient != null) { rtpUdpClient.Close(); rtpUdpClient = null; } running = false; } public void RunDeviceBefore() { } public void RunDeviceAfter() { // if (running) // { // Console.WriteLine("Time={0} IP={1} scp={2}", StateMachine.Time, IPAddress, scpConnected ? "connected" : "disconnected"); // } } /// /// Detect the camera with specified hardware address (DIP switches) /// /// Hardware address (DIP switches) /// true = Broadcast 'SetDateTime' to all cameras /// Detected IP address /// Detected hardware /// Detected HW revision /// Detected serial number /// Detected OS image /// true when camera detected successfully 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.Info(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 { connectionInfo = new PasswordConnectionInfo(IPAddress.ToString(), ClpUserName, ClpPassword); scpClient = new ScpClient(connectionInfo); scpClient.Connect(); scpConnected = scpClient.IsConnected; } catch (Exception) { scpConnected = false; } if (scpConnected) { while (!stopScpThreadFlag) { switch (scpCommand) { case ScpCommand.Get: scpCommand = ScpCommand.None; scpClient.Download(scpSrcFileName, new System.IO.FileInfo(scpDstFileName)); break; default: Thread.Sleep(250); break; } } scpClient.Disconnect(); scpConnected = false; } } /// /// Transfer a file via SCP /// /// /// 0 (success), -1 (busy), -2 (no session) public int TransferFileViaScp(string srcFileName, string dstFileName) { if (!scpConnected) return -2; if (scpCommand != ScpCommand.None) return -1; scpSrcFileName = srcFileName; scpDstFileName = dstFileName; scpCommand = ScpCommand.Get; 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 the listener thread: {0}", exc.Message); log.Error(msg); } } return; } 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.ToString(), 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) { //new MeasurementOp(this, NetAdapter.IPAddress.ToString(), measurementUdpPortNr, roiParams); return this; } else { return null; } } string command; bool measurementCommandSent; bool connectionEstablished; public void Start() { /// Prepare the telent command StringBuilder sb = new StringBuilder(); foreach (var s in roiParams) sb.AppendFormat(" {0}", s); command = string.Format("clp/Measurement -udp {0} {1}{2}{3} 1 7 0 0 0", NetAdapter.IPAddress, measurementUdpPortNr, CameraCfg.UseTestImages ? (" -l " + CameraCfg.TestImagesCount.ToString()) : string.Empty, sb); measurementCommandSent = false; connectionEstablished = false; /// If ready send (=enqueue) the command if (Telnet.State == TelnetClient.TelnetState.Inactive) { 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) { Telnet.Enqueue(new Telnet.Command(CmdAction.SEND_MSRMNT_CMD, command)); measurementCommandSent = true; } } else if (!connectionEstablished) { connectionEstablished = true; } return Event.None; } public void Stop() { if (measurementCommandSent) { Telnet.Enqueue(new Telnet.Command(CmdAction.SEND_COMMAND, TelnetClient.CtrlCCommand)); } } 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); } } } } void MeasurementUdpListener() { StringBuilder toBeProcessed = new StringBuilder(); IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, measurementUdpPortNr); try { while (true) { byte[] byteArray = measurementListener.Receive(ref ipEndPoint); toBeProcessed.Append(Encoding.ASCII.GetString(byteArray, 0, byteArray.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[] { ';' }); 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); } } } toBeProcessed.Remove(0, from + len + 2); } } } catch { log.FatalFormat("Camera : MeasurementUdpListener() thread failed"); } } } }