diff --git a/TBF.sln.DotSettings.user b/TBF.sln.DotSettings.user index 651fe81c0..e90914110 100644 --- a/TBF.sln.DotSettings.user +++ b/TBF.sln.DotSettings.user @@ -1,18 +1,29 @@  + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded True 77EB589F-C670-4489-AAD6-2A3C02061FD1 77EB589F-C670-4489-AAD6-2A3C02061FD1 d6790ab7-33c2-4425-b2c9-51480cd1a852 - <SessionState ContinuousTestingMode="0" Name="GetCorrection" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="GetCorrection" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Entities.MeasurementCorrectionTest.GetCorrection</TestId> <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Rig.Modbus.Meret.AdjustableScale.AdjustableMeterTest.GetCorrection</TestId> <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Rig.Network.Camera.KeyenceIV3G120.CameraTest.RtpListener</TestId> <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Rig.Network.Camera.KeyenceIV3G120.CameraTest.Initialize</TestId> + <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Rig.Network.Camera.CJMS11.CameraTest.Initialize</TestId> </TestAncestor> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="Initialize" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" Name="Initialize" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> <TestId>MSTest::77EB589F-C670-4489-AAD6-2A3C02061FD1::.NETFramework,Version=v4.7.2::TBFTests.Rig.Network.Camera.KeyenceIV3G120.CameraTest.Initialize</TestId> </TestAncestor> @@ -22,17 +33,21 @@ False - True + False False - True + False False - True - True - True + False + False + False - True + False + True + False + + False False - True + False True False diff --git a/TBF/Rig/Network/Camera/CJMS11/Camera.cs b/TBF/Rig/Network/Camera/CJMS11/Camera.cs new file mode 100644 index 000000000..589b442c3 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/Camera.cs @@ -0,0 +1,999 @@ +/// +/// Copyright (c) 2017-2022 Sensus Slovensko a.s. +/// +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using log4net; +using Renci.SshNet; +using Common; +using TBF.Rig.Network.Telnet; +using TBF.Resources; +using TBF.Rig.Network.Camera.CJMS11.POJO; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + /// + /// 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}, 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)); + } + + + private const int iPort = 32456; + 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 bool ShowOverlayRect { set { showOverlayRect = value; } } + bool showOverlayRect = false; + + public Rectangle OverlayRect { set { overlayRect = value; } } + Rectangle overlayRect; + + public int OverlayThickness { set { overlayThickness = value; } } + int overlayThickness; + + public int CameraIdx { get { return cameraIdx; } } + int cameraIdx; + /// + static int nextCameraIdx = 0; + + static int GetCameraIdx() + { + return 2; + } //nextCameraIdx++; } /// Used in Initialize() + + /// + /// 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 rtpTcpLocalPort; + TcpClient rtpTcpClient; + Thread rtpListenerThread; + NetworkStream rtpStream = null; + StreamReader rtpReader = null; + 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 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"); + } + + + public override void Initialize() + { + cameraIdx = GetCameraIdx(); + + running = false; + Telnet = null; + terminalDlg = null; + roisAndResults = new RoisAndResults(); /// object for measurement results, writes/reads to/from this object are locked + + // if (CameraCfg.DebugLevel == DebugMode.Simulate) + // { + // UiBridge.Bridge.OnCameraInfo(cameraIdx, string.Format("{0} s/n {1} si simulated", Name, CameraCfg.HardwareAddress)); + // return; + // } + + /// Detect a camera + bool cameraDetected = DetectJMSCamera(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(cameraIdx, string.Format("{0} ver.{1} {2} s/n {3}", Name, sdCardVer, ipAddress, CameraCfg.HardwareAddress)); + + ClpUserName = (sdCardVer.Length > 0 && sdCardVer[0] == '1') ? "tbf" : "pi"; + + StartRtpListener(IPAddress, iPort); + StartMsrmntListener(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}' detected: Id={1} addr={2} IP={3} SD={4}", Name, CameraIdx, 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}' NOT detected: Id={1} addr={2}", Name, CameraIdx, 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.IsAlive) scpThread.Join(50); + if (rtpListenerThread != null) rtpListenerThread.Join(50); + if (msrmntListenerThread != null) msrmntListenerThread.Join(50); + + if (rtpTcpClient != null) + { + rtpTcpClient.Close(); + rtpTcpClient = 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 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; + CameraCfg.IPAddressCJMS = tmpcfg.IPAddressCJMS; + } + } + }; + } + + #endregion Configuration Change Handling + + + /// + /// Detect the camera with specified hardware address (DIP switches). + /// Optionally set camera date and time. + /// + /// 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.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 + } + + bool DetectJMSCamera(int hwAddress, ref IPAddress ipAddress, bool setDateTime, out string hardware, out string revision, out string serial, out string sdCardVer) + { + string command_CFG = "get_cfg"; + if (CameraCfg.IPAddressCJMS != null) + { + TcpClient tcpClient = new TcpClient(); + // Get network stream + NetworkStream stream = null; + StreamReader reader = null; + + try + { + + + tcpClient.Connect(CameraCfg.IPAddressCJMS, iPort); + log.Debug(string.Format("Camera connected to server {0}:{1}.", CameraCfg.IPAddressCJMS, iPort)); + ipAddress = IPAddress.Parse(CameraCfg.IPAddressCJMS); + // Get network stream + stream = tcpClient.GetStream(); + stream.ReadTimeout = 1000; + reader = new StreamReader(stream, Encoding.ASCII); + + // Send command + DateTime detectionStart = DateTime.Now; + byte[] commandBytes = Encoding.ASCII.GetBytes(command_CFG); + stream.Write(commandBytes, 0, commandBytes.Length); + Console.WriteLine("Command sent."); + + + string responseAnswer = ""; + try + { + int iReads = 0; + while (true) + { + //get config + responseAnswer = reader.ReadLine(); + log.Debug(string.Format("received from camera: {0}", responseAnswer)); + + + if (responseAnswer.Contains(command_CFG)) + { + break; + } + + if (iReads > 5) + { + log.Error($"Maximum Count of unexcepted answers excited!"); + break; + } + + iReads++; + } + } + catch (IOException ioEx) + { + log.Error($"Timeout or IO error while reading from camera: {ioEx.Message}"); + } + catch (Exception e) + { + log.Error(string.Format("Error while reading from camera: {0}", e.Message)); + } + + double duration_ms = (DateTime.Now - detectionStart).TotalMilliseconds; + + hardware = string.Empty; + revision = string.Empty; + serial = string.Empty; + sdCardVer = string.Empty; + + 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(), + responseAnswer); + 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); + } + finally + { + // Clean up + if (reader != null) + reader.Close(); + if (stream != null) + stream.Close(); + if (tcpClient.Connected) + tcpClient.Close(); + } + + + } + + if (CameraCfg.DebugLevel == DebugMode.Simulate) + { + ipAddress = null; + hardware = null; + revision = null; + serial = null; + sdCardVer = null; + return true; + } + + 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; + } + } + + /// + /// Download a file from the camera via SCP + /// + /// + /// 0 (success), -1 (busy), -2 (no session) + 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; + } + + /// + /// Upload a file to the camera via SCP + /// + /// + /// 0 (success), -1 (busy), -2 (no session) + 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; + } + + + /// + /// listener for multiple - repetetive image receiving + /// + /// + /// method uses reader to receive data from camera + /// + /// + /// + + private void StartRtpListener(IPAddress localIP, int localPort) + { + rtpTcpClient = new TcpClient(new IPEndPoint(localIP, localPort)); + rtpListenerThread = new Thread(new ThreadStart(RtpListener)); + rtpListenerThread.Start(); + } + + void RtpListener() + { + JpegFrame jpegFrame = new JpegFrame(); + bool synced = false; + int penWidth = 1; + Pen pen = new Pen(Color.Green, penWidth); + + rtpStream = rtpTcpClient.GetStream(); + rtpStream.ReadTimeout = 1000; + rtpReader = new StreamReader(rtpStream, Encoding.ASCII); + + while (!stopRtpListenerFlag) + { + try + { + string responseAnswer = rtpReader.ReadLine(); + + if (responseAnswer.Length > 0) + { + /// Process the packet + JmsPacket newPacket = new JmsPacket(responseAnswer); + // if (synced) + // { + /// Sychronized: process the new packet + switch (newPacket.JmsMessage.Command) + { + case CommandM.grab_image_: + ParsedImage parsedImage = newPacket.JmsMessage.getImage(); + if (parsedImage.Encoding == ImageType.BASE_64) + { + Image image = parsedImage.Image; + + + if (showOverlayRect && overlayRect != null && overlayThickness > 0) + { + using (var grph = Graphics.FromImage(image)) + { + if (penWidth != overlayThickness) + { + penWidth = overlayThickness; + pen = new Pen(Color.Green, penWidth); + } + + grph.DrawRectangle(pen, overlayRect); + } + } + + UiBridge.Bridge.OnImage(this.cameraIdx, image); + } + break; + case CommandM.close_: + 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, rtpTcpLocalPort); + //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)); + } + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/CameraCfg.cs b/TBF/Rig/Network/Camera/CJMS11/CameraCfg.cs new file mode 100644 index 000000000..256e60025 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/CameraCfg.cs @@ -0,0 +1,74 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Xml.Serialization; +using Common; +using TBF.Rig.Generic; +using TBF.Resources; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + 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 cmpntEntities) { return new CameraCfgCtrl(); } + + /// + /// Serialized parameters + /// + public int HardwareAddress; + public bool DisplayTerminal; + + [XmlIgnore] + public TestImagesMode TestImagesMode; + + [XmlIgnore] + public int TestImagesCount; + + public string IPAddressCJMS; + + + /// 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 = DebugMode.AutoDetect; + IPAddressCJMS = "192.168.1.100"; + } + + public string ToString(int i) + { + return string.Format("Name={0}, s/n={1}, Terminal={2}, TestImagesMode={3}, TestImagesCount={4}, Parent={5}, IPAddressCJMS={6}", + Name, + HardwareAddress, + DisplayTerminal ? Strings.yes : Strings.no, + TestImagesMode, + TestImagesCount, + ParentName, + IPAddressCJMS); + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.cs b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.cs new file mode 100644 index 000000000..d935c5f31 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.cs @@ -0,0 +1,276 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// + +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Castle.Components.DictionaryAdapter.Xml; +using Common; +using Config.Entities; +using log4net; +using TBF.Rig.Generic; +using TBF.Resources; +using TBF.Rig.Network.Adapter; +using TBF.UI.Bench.Components; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public partial class CameraCfgCtrl : UserControl, IComponentCfgCtrl + { + static readonly ILog log = LogManager.GetLogger(typeof(CameraCfgCtrl)); + ComponentParametersDlg parent; + + private bool isIPCalculated = false; + + 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(); + if (!(isIPCalculated && (config.IPAddressCJMS == null || config.IPAddressCJMS.Equals("")))) + { + ipAddressTextBox.Text = config.IPAddressCJMS; + } + } + + public void Unlock() + { + nameTextBox.Enabled = true; + parentNameComboBox.Enabled = true; + hwAddressTextBox.Enabled = true; + displayTerminalCheckBox.Enabled = true; + testImagesModeComboBox.Enabled = true; + testImagesCountTextBox.Enabled = true; + ipAddressTextBox.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.IPAddressCJMS != ipAddressTextBox.Text && !ipAddressTextBox.Text.Contains("?")) + ) + { + config.Name = nameTextBox.Text; + config.ParentName = newParentName; + config.HardwareAddress = newHWAddress; + config.DisplayTerminal = displayTerminalCheckBox.Checked; + if (!ipAddressTextBox.Text.Contains("?")) + { + config.IPAddressCJMS = ipAddressTextBox.Text; + } + + 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; + } + + + private void parentNameComboBox_TextChanged(object sender, EventArgs e) + { + if (parent == null) return; + + if (parent.CmpntEntities != null) + { + foreach (var cmpnt in parent.CmpntEntities) + { + IComponentFactory cmpntFactoryFromClassName = + TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName); + if (cmpntFactoryFromClassName is TBF.Rig.Network.Adapter.Factory) + { + NetadapterCfg netadapterCfg = + cmpntFactoryFromClassName.CmpntCfgFromCmpntEntity(cmpnt) as NetadapterCfg; + string adapterDescription = netadapterCfg.Description; + AdapterInfo netadapter = AdapterInfo.GetNetAdapter(adapterDescription); + ipAddressAdapterTextBox.Text = netadapter.IPAddress.ToString(); + if (config.IPAddressCJMS == null || config.IPAddressCJMS.Equals("")) + { + ipAddressTextBox.Text = getAdatpterIP(netadapter); + } + + isIPCalculated = true; + break; + } + } + } + } + + private string getAdatpterIP(AdapterInfo netadapter) + { + List ipSegments = GetIPAddressInSegments(netadapter.IPAddress.ToString()); + + string address = string.Format( + "{0}.{1}.{2}.???", + ipSegments[0], + ipSegments[1], + ipSegments[2] + ); + + return address; + } + + private List GetIPAddressInSegments(string ipAddress) + { + List ipAddressSegments = new List(); + // Regular expression to match IPv4 and capture 4 segments + string pattern = @"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$"; + + Match match = Regex.Match(ipAddress, pattern); + + if (match.Success) + { + ipAddressSegments.Add(match.Groups[1].Value); + ipAddressSegments.Add(match.Groups[2].Value); + ipAddressSegments.Add(match.Groups[3].Value); + ipAddressSegments.Add(match.Groups[4].Value); + } + else + { + log.Error("Invalid IP address format."); + } + + return ipAddressSegments; + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.designer.cs b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.designer.cs new file mode 100644 index 000000000..ab7ed8822 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.designer.cs @@ -0,0 +1,260 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +namespace TBF.Rig.Network.Camera.CJMS11 +{ + partial class CameraCfgCtrl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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.ipAddressTextBox = new System.Windows.Forms.TextBox(); + this.iPAddressLabel = new System.Windows.Forms.Label(); + this.ipAddressAdapterTextBox = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // hwAddressTextBox + // + this.hwAddressTextBox.Enabled = false; + this.hwAddressTextBox.Location = new System.Drawing.Point(198, 209); + this.hwAddressTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.hwAddressTextBox.Name = "hwAddressTextBox"; + this.hwAddressTextBox.Size = new System.Drawing.Size(193, 26); + this.hwAddressTextBox.TabIndex = 6; + // + // hwAddressLabel + // + this.hwAddressLabel.AutoSize = true; + this.hwAddressLabel.Location = new System.Drawing.Point(42, 213); + this.hwAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.hwAddressLabel.Name = "hwAddressLabel"; + this.hwAddressLabel.Size = new System.Drawing.Size(107, 20); + this.hwAddressLabel.TabIndex = 5; + this.hwAddressLabel.Text = "Serial number"; + // + // parentNameLabel + // + this.parentNameLabel.AutoSize = true; + this.parentNameLabel.Location = new System.Drawing.Point(42, 106); + this.parentNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.parentNameLabel.Name = "parentNameLabel"; + this.parentNameLabel.Size = new System.Drawing.Size(100, 20); + this.parentNameLabel.TabIndex = 3; + this.parentNameLabel.Text = "Parent name"; + // + // nameTextBox + // + this.nameTextBox.Enabled = false; + this.nameTextBox.Location = new System.Drawing.Point(198, 63); + this.nameTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.nameTextBox.Name = "nameTextBox"; + this.nameTextBox.Size = new System.Drawing.Size(193, 26); + this.nameTextBox.TabIndex = 2; + // + // nameLabel + // + this.nameLabel.AutoSize = true; + this.nameLabel.Location = new System.Drawing.Point(42, 68); + this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.nameLabel.Name = "nameLabel"; + this.nameLabel.Size = new System.Drawing.Size(51, 20); + this.nameLabel.TabIndex = 1; + this.nameLabel.Text = "Name"; + // + // classNameLabel + // + this.classNameLabel.AutoSize = true; + this.classNameLabel.Location = new System.Drawing.Point(194, 26); + this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.classNameLabel.Name = "classNameLabel"; + this.classNameLabel.Size = new System.Drawing.Size(125, 20); + this.classNameLabel.TabIndex = 0; + this.classNameLabel.Text = "ComonentName"; + // + // parentNameComboBox + // + this.parentNameComboBox.Enabled = false; + this.parentNameComboBox.FormattingEnabled = true; + this.parentNameComboBox.Location = new System.Drawing.Point(198, 102); + this.parentNameComboBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.parentNameComboBox.Name = "parentNameComboBox"; + this.parentNameComboBox.Size = new System.Drawing.Size(193, 28); + this.parentNameComboBox.TabIndex = 4; + this.parentNameComboBox.TextChanged += new System.EventHandler(this.parentNameComboBox_TextChanged); + // + // displayTerminalCheckBox + // + this.displayTerminalCheckBox.AutoSize = true; + this.displayTerminalCheckBox.Enabled = false; + this.displayTerminalCheckBox.Location = new System.Drawing.Point(200, 255); + this.displayTerminalCheckBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.displayTerminalCheckBox.Name = "displayTerminalCheckBox"; + this.displayTerminalCheckBox.Size = new System.Drawing.Size(146, 24); + 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(198, 335); + this.testImagesCountTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.testImagesCountTextBox.Name = "testImagesCountTextBox"; + this.testImagesCountTextBox.Size = new System.Drawing.Size(67, 26); + this.testImagesCountTextBox.TabIndex = 12; + // + // testImagesCountLabel + // + this.testImagesCountLabel.AutoSize = true; + this.testImagesCountLabel.Location = new System.Drawing.Point(42, 339); + this.testImagesCountLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.testImagesCountLabel.Name = "testImagesCountLabel"; + this.testImagesCountLabel.Size = new System.Drawing.Size(139, 20); + 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(198, 295); + this.testImagesModeComboBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.testImagesModeComboBox.Name = "testImagesModeComboBox"; + this.testImagesModeComboBox.Size = new System.Drawing.Size(193, 28); + this.testImagesModeComboBox.TabIndex = 14; + // + // testImagesModeLabel + // + this.testImagesModeLabel.AutoSize = true; + this.testImagesModeLabel.Location = new System.Drawing.Point(42, 299); + this.testImagesModeLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.testImagesModeLabel.Name = "testImagesModeLabel"; + this.testImagesModeLabel.Size = new System.Drawing.Size(139, 20); + this.testImagesModeLabel.TabIndex = 13; + this.testImagesModeLabel.Text = "Test images mode"; + // + // ipAddressTextBox + // + this.ipAddressTextBox.Enabled = false; + this.ipAddressTextBox.Location = new System.Drawing.Point(198, 176); + this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.ipAddressTextBox.Name = "ipAddressTextBox"; + this.ipAddressTextBox.Size = new System.Drawing.Size(193, 26); + this.ipAddressTextBox.TabIndex = 15; + // + // iPAddressLabel + // + this.iPAddressLabel.AutoSize = true; + this.iPAddressLabel.Location = new System.Drawing.Point(42, 176); + this.iPAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.iPAddressLabel.Name = "iPAddressLabel"; + this.iPAddressLabel.Size = new System.Drawing.Size(89, 20); + this.iPAddressLabel.TabIndex = 16; + this.iPAddressLabel.Text = "IP address:"; + // + // ipAddressAdapterTextBox + // + this.ipAddressAdapterTextBox.Enabled = false; + this.ipAddressAdapterTextBox.Location = new System.Drawing.Point(200, 140); + this.ipAddressAdapterTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.ipAddressAdapterTextBox.Name = "ipAddressAdapterTextBox"; + this.ipAddressAdapterTextBox.Size = new System.Drawing.Size(193, 26); + this.ipAddressAdapterTextBox.TabIndex = 17; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(42, 140); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(150, 20); + this.label1.TabIndex = 18; + this.label1.Text = "Adapter IP address:"; + // + // CameraCfgCtrl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.label1); + this.Controls.Add(this.ipAddressAdapterTextBox); + this.Controls.Add(this.iPAddressLabel); + this.Controls.Add(this.ipAddressTextBox); + 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.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.Name = "CameraCfgCtrl"; + this.Size = new System.Drawing.Size(603, 391); + this.Load += new System.EventHandler(this.PumpCfgCtrl_Load); + this.ResumeLayout(false); + this.PerformLayout(); + } + + private System.Windows.Forms.Label label1; + + private System.Windows.Forms.TextBox ipAddressAdapterTextBox; + + private System.Windows.Forms.TextBox ipAddressTextBox; + private System.Windows.Forms.Label iPAddressLabel; + + #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; + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.resx b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/CameraCfgCtrl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/Factory.cs b/TBF/Rig/Network/Camera/CJMS11/Factory.cs new file mode 100644 index 000000000..d7dfd7558 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/Factory.cs @@ -0,0 +1,25 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System.Collections.Generic; +using TBF.Rig.Generic; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public class Factory : IComponentFactory + { + public string ClassName { get { return GetType().Namespace.Substring(8); } } + public override string ToString() { return ClassName; } + + public IComponent DummyComponent() { return new Camera(); } + + public IComponent GetComponent(IComponentCfg cfg, IList 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); + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs b/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs new file mode 100644 index 000000000..2a492fd31 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs @@ -0,0 +1,224 @@ +/// +/// Copyright (c) 2017 Sensus Slovensko a.s. +/// +using System; +using System.Diagnostics; +using log4net; +using Common; +using Config.Entities; +using TBF.Rig.Network.Telnet; +using System.IO; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + 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; + + + /// + /// Events: Event.None or Event.Error + /// + /// CLP1611.Camera reference + 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 == DebugMode.Simulate || camera.CameraCfg.DebugLevel == 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 == DebugMode.Simulate || + camera.CameraCfg.DebugLevel == 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 == 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); + } + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs b/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs new file mode 100644 index 000000000..d5fc42f2c --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs @@ -0,0 +1,54 @@ +using TBF.Rig.Network.Camera.CJMS11.POJO; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public class JmsMessage + { + private POJO.MessageStatus status; + private CommandM command; + private string payload; + + private string orig_status; + private string orig_command; + private string orig_payload; + + public POJO.MessageStatus Status + { + get => status; + set => status = value; + } + + public CommandM Command + { + get => command; + set => command = value; + } + + public string Payload + { + get => payload; + set => payload = value; + } + + public JmsMessage(string status, string command, string payload) + { + this.status = MessageStatusEnum.GetCommandM(status); + this.command = CommandMEnum.GetCommandM(command); + + orig_status = status; + orig_command = command; + orig_payload = payload; + } + + public bool isMessageImageType(){ + return this.command == CommandM.grab_image_; + } + + public ParsedImage getImage(){ + if(isMessageImageType()){ + return JmsPacket.ImageParser(this.payload); + } + return null; + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs b/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs new file mode 100644 index 000000000..74d6cea51 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs @@ -0,0 +1,90 @@ +using System; +using System.Text.RegularExpressions; +using log4net; +using TBF.Rig.Network.Camera.CJMS11.POJO; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public class JmsPacket + { + private static readonly ILog log = LogManager.GetLogger(typeof(JmsPacket)); + + private JmsMessage jmsMessage; + + public JmsMessage JmsMessage + { + get => jmsMessage; + set => jmsMessage = value; + } + + public JmsPacket(string message) + { + string received_message = message; + + try + { + if (ParseMessage(received_message)) + { + //success + } + + } + catch (Exception e) + { + log.Error("Error parsing message: " + e.Message); + } + } + + private bool ParseMessage(string message) + { + message = message.Trim(); + if (message.Length < 1) return false; + + this.jmsMessage = JmsMessageFromString(message); + + return true; + } + + public static JmsMessage JmsMessageFromString(string message) + { + // Equivalent regex pattern + var pattern = new Regex(@"^(ACK|NACK):\s*(\S+)\s*(?:~(.*)~)?\s*END$", RegexOptions.Singleline); + + var match = pattern.Match(message); + if (match.Success) + { + string status = match.Groups[1].Value; + string commandStr = match.Groups[2].Value; + string payload = match.Groups[3].Success ? match.Groups[3].Value : ""; + + return new JmsMessage(status, commandStr, payload); + } + else + { + throw new ArgumentException($"Message format not recognized: {message}"); + } + + return null; + } + + public static ParsedImage ImageParser(string payloadImage) + { + // Equivalent regex pattern + var imagePattern = new Regex(@"IMAGE:([A-Z0-9_]+):\s*(.*?)\s*~IMAGE_END", RegexOptions.Singleline); + + var match = imagePattern.Match(payloadImage); + if (match.Success) + { + string encoding = match.Groups[1].Value; + string imageData = match.Groups[2].Value; + + return new ParsedImage(ImageTypeEnum.GetCommandM(encoding), imageData); + } + else + { + throw new ArgumentException($"Payload does not match expected image format: {payloadImage}"); + } + } + } + +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/LiveStreamOp.cs b/TBF/Rig/Network/Camera/CJMS11/LiveStreamOp.cs new file mode 100644 index 000000000..ef7a98cef --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/LiveStreamOp.cs @@ -0,0 +1,68 @@ +using System; +using System.Diagnostics; +using log4net; +using Common; +using TBF.Rig.Network.Telnet; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + 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; + + /// + /// Events: Event.None or Event.Error + /// + /// CLP1611.Camera reference + 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 == 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 == 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); + } + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/CommandM.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/CommandM.cs new file mode 100644 index 000000000..578db4280 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/CommandM.cs @@ -0,0 +1,22 @@ +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public enum CommandM + { + noCommand, + unknown_, // special: only return - unknown command + connect_, // special: only return - TCP connection created + type_camera_, + status_, + prepare_camera_, + grab_image_, + send_, + start_stream_, + stop_stream_, + get_cfg_, + set_cfg_, + close_, + disconnect_, + overload_config_, + client_count_ + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/CommandMEnum.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/CommandMEnum.cs new file mode 100644 index 000000000..f17f7aed2 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/CommandMEnum.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public static class CommandMEnum + { + private static readonly Dictionary commandToString = new Dictionary + { + { CommandM.noCommand, "noCommand" }, + { CommandM.unknown_, "unknown" }, + { CommandM.connect_, "connect" }, + { CommandM.type_camera_, "type_camera" }, + { CommandM.status_, "status" }, + { CommandM.prepare_camera_, "prepare_camera" }, + { CommandM.grab_image_, "grab_image" }, + { CommandM.send_, "send" }, + { CommandM.start_stream_, "start_stream" }, + { CommandM.stop_stream_, "stop_stream" }, + { CommandM.get_cfg_, "get_cfg" }, + { CommandM.set_cfg_, "set_cfg" }, + { CommandM.close_, "close" }, + { CommandM.disconnect_, "disconnect" }, + { CommandM.overload_config_, "overload_config" }, + { CommandM.client_count_, "client_count" } + }; + + + private static readonly Dictionary stringToCommand = commandToString.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + + public static string GetVal(CommandM cmd) + { + return commandToString.TryGetValue(cmd, out var str) ? str : "unknown"; + } + + public static CommandM GetCommandM(string token) + { + if (string.IsNullOrEmpty(token)) + return CommandM.noCommand; + + return stringToCommand.TryGetValue(token, out var cmd) ? cmd : CommandM.noCommand; + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/ImageType.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/ImageType.cs new file mode 100644 index 000000000..fc4c28240 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/ImageType.cs @@ -0,0 +1,10 @@ +namespace TBF.Rig.Network.Camera.CJMS11.POJO +{ + public enum ImageType + { + UNKNOWN, + BASE_64, + MJPEG, + RGB888 + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/ImageTypeEnum.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/ImageTypeEnum.cs new file mode 100644 index 000000000..6082a0f89 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/ImageTypeEnum.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; + +namespace TBF.Rig.Network.Camera.CJMS11.POJO +{ + public class ImageTypeEnum + { + private static readonly Dictionary commandToString = new Dictionary + { + { ImageType.BASE_64, "BASE_64" }, + { ImageType.MJPEG, "MJPEG" }, + { ImageType.RGB888, "RGB888" }, + { ImageType.UNKNOWN, "" } + }; + + private static readonly Dictionary stringToCommand = commandToString.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + + public static string GetVal(ImageType cmd) + { + return commandToString.TryGetValue(cmd, out var str) ? str : "unknown"; + } + + public static ImageType GetCommandM(string token) + { + if (string.IsNullOrEmpty(token)) + return ImageType.UNKNOWN; + + return stringToCommand.TryGetValue(token, out var cmd) ? cmd : ImageType.UNKNOWN; + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatus.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatus.cs new file mode 100644 index 000000000..d43a23684 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatus.cs @@ -0,0 +1,10 @@ +namespace TBF.Rig.Network.Camera.CJMS11.POJO +{ + public enum MessageStatus + { + UNDEFINED, + ACK, + NACK, + ERROR + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatusEnum.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatusEnum.cs new file mode 100644 index 000000000..fd9ba8140 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/MessageStatusEnum.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; + +namespace TBF.Rig.Network.Camera.CJMS11.POJO +{ + public class MessageStatusEnum + { + private static readonly Dictionary commandToString = new Dictionary + { + { MessageStatus.UNDEFINED, "" }, + { MessageStatus.ACK, "ACK" }, + { MessageStatus.NACK, "NACK" }, + { MessageStatus.ERROR, "ERROR" } + }; + + private static readonly Dictionary stringToCommand = commandToString.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + + public static string GetVal(MessageStatus cmd) + { + return commandToString.TryGetValue(cmd, out var str) ? str : "undefined"; + } + + public static MessageStatus GetCommandM(string token) + { + if (string.IsNullOrEmpty(token)) + return MessageStatus.UNDEFINED; + + return stringToCommand.TryGetValue(token, out var cmd) ? cmd : MessageStatus.UNDEFINED; + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/ParsedImage.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/ParsedImage.cs new file mode 100644 index 000000000..8f9ae5c96 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/ParsedImage.cs @@ -0,0 +1,38 @@ +using System; +using TBF.Rig.Network.Camera.CJMS11.POJO; +using System.Drawing; +using System.IO; + + + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public class ParsedImage + { + public ImageType Encoding { get; } + public string ImageData { get; } + + public Image Image { get { return (ImageData.Length>0 ? Base64ToImage(ImageData) : null); } } + + public ParsedImage(ImageType encoding, string imageData) + { + Encoding = encoding; + ImageData = imageData; + } + + public override string ToString() + { + return $"ParsedImage{{encoding='{Encoding}', imageData='{ImageData}'}}"; + } + + public static Image Base64ToImage(string base64Image) + { + byte[] imageBytes = Convert.FromBase64String(base64Image); + + using (var ms = new MemoryStream(imageBytes)) + { + return Image.FromStream(ms); + } + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/RoisAndResults.cs b/TBF/Rig/Network/Camera/CJMS11/RoisAndResults.cs new file mode 100644 index 000000000..c99e092f2 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/RoisAndResults.cs @@ -0,0 +1,91 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System; +using System.Collections.Generic; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public class RoisAndResults + { + public const int MaxRegisteredRois = 4; /// Max. count of registered (and measured) ROI-s + + public long CameraTimeMs; + public int[] Angles; + + private IList roiParams; + public int RegisteredRoisCount { get { return roiParams.Count; } } + + + public RoisAndResults() + { + Angles = new int[MaxRegisteredRois]; + roiParams = new List(); + } + + + 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; + } + } + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.Designer.cs b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.Designer.cs new file mode 100644 index 000000000..7c95301b4 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.Designer.cs @@ -0,0 +1,62 @@ +namespace TBF.Rig.Network.Camera.CJMS11 +{ + partial class TerminalDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.cs b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.cs new file mode 100644 index 000000000..ee4789a0e --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.cs @@ -0,0 +1,37 @@ +using System; +using System.Windows.Forms; +using TBF.Rig.Network.Telnet; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + public partial class TerminalDlg : Form + { + TelnetClient telnet; + + 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(OnTextChanged), sndr, args); } + else OnTextChanged(sndr, args); + }; + } + + void OnTextChanged(object sndr, VtTextChangedEventArgs args) + { + vtTextBox.Text = args.VtText; + } + } +} diff --git a/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.resx b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/TerminalDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/Roi/RoiCfgCtrl.cs b/TBF/Rig/Network/Camera/Roi/RoiCfgCtrl.cs index 4a6bc9d74..24464e050 100644 --- a/TBF/Rig/Network/Camera/Roi/RoiCfgCtrl.cs +++ b/TBF/Rig/Network/Camera/Roi/RoiCfgCtrl.cs @@ -42,14 +42,10 @@ namespace TBF.Rig.Network.Camera.Roi { foreach (var cmpnt in parent.CmpntEntities) { - if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.Rig.Network.Camera.CLP1611.Factory) + if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.Rig.Network.Camera.CJMS11.Factory) { parentNameComboBox.Items.Add(cmpnt.Name); } - if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.Rig.Network.Camera.Roi.Factory) - { - previousRoiComboBox.Items.Add(cmpnt.Name); - } } } diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Factory.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Factory.cs new file mode 100644 index 000000000..924b756b9 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Factory.cs @@ -0,0 +1,25 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System.Collections.Generic; +using TBF.Rig.Generic; + +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + public class Factory : IComponentFactory + { + public string ClassName { get { return this.GetType().Namespace.Substring(8); } } + public override string ToString() { return ClassName; } + + public IComponent DummyComponent() { return new Roi(); } + + public IComponent GetComponent(IComponentCfg cfg, IList components) { return new Roi(cfg, components); } + + public IComponentCfg DefaultConfig() { return new RoiCfg(this.GetType().Namespace.Substring(8), this); } + + public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component) + { + return ComponentCfgBase.CreateFromDbEntity(RoiCfg.Serializer, component, this); + } + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/ProcedureParams.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/ProcedureParams.cs new file mode 100644 index 000000000..f36571ebe --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/ProcedureParams.cs @@ -0,0 +1,115 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System.IO; +using System.Xml.Serialization; +using Common; +using Config.Entities; +using TBF.Rig.Generic; +using TBF.Resources; + +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + public class ProcedureParams : ProcedureParamsBase, IParamsProvider, IProcedureParams + { + public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ProcedureParams) })[0]; + public override XmlSerializer GetSerializer() { return Serializer; } + + public double PulsesPerLtr; /// [l^-1] + + public override void InitializeAll() + { + PulsesPerLtr = 1.0; + } + + string[] paramNames = new string[] + { + Strings.PulsesPerLtr, + }; + public override string ParamName(int i) { return paramNames[i]; } + public override int ParamsCount() { return paramNames.Length; } + + public override string ToString(int i) + { + switch (i) + { + case 0: return PulsesPerLtr.ToString(); + default: return string.Empty; + } + } + + /// Retrieves parameters from UI controls + public CfgUpdateFlags UpdateParam(int i, string strValue) + { + switch (i) + { + case 0: PulsesPerLtr = Utils.ParseUDouble(strValue); return CfgUpdateFlags.None; + default: return CfgUpdateFlags.None; + } + } + + /// Verifies whether strings in UI controls represent valid parameters + public bool ValidateParam(int i, string strValue, out string message) + { + message = string.Empty; + + double dummy; + switch (i) + { + case 0: + if (Utils.TryParseUDouble(strValue, out dummy)) return true; + break; + default: + message = "Invalid index"; + return false; + } + + message = ParamName(i) + " is invalid"; + return false; + } + + void CopyContentTo(ProcedureParams prms) + { + prms.PulsesPerLtr = this.PulsesPerLtr; + } + + public IParamsProvider Clone() + { + ProcedureParams pars = new ProcedureParams(); + CopyContentTo(pars); + return pars; + } + + public override void UpdateFromDbEntity(ComponentProcedure dbEntity) + { + if (dbEntity == null) return; + try + { + ProcedureParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcedureParams; + + procedureParamsEntity = dbEntity; + componentName = dbEntity.CmpntName; + procedure = dbEntity.Procedure; + + if (tmp != null) tmp.CopyContentTo(this); + } + catch + { + } + } + + public ProcedureParams() { } + + public ProcedureParams(bool initialize) + { + if (initialize) InitializeAll(); + } + + public ProcedureParams(ComponentProcedure procedureParamsEntity, string componentName, Procedure procedure) + { + this.procedureParamsEntity = procedureParamsEntity; + this.componentName = componentName; + this.procedure = procedure; + } + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Roi.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Roi.cs new file mode 100644 index 000000000..f69318cd2 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/Roi.cs @@ -0,0 +1,139 @@ +/// +/// Copyright (c) 2017-2021 Sensus Metering Systems +/// +using System; +using System.Collections.Generic; +using System.Text; +using log4net; +using Common; +using Config.Entities; +using TBF.Rig.GenericDevices; + +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + public class Roi : ComponentBase, GenericDevices.IRegReaderStillCamera + { + /// TODO: Implement support for sharing one camera by multiple ROI-s + + private static readonly ILog log = LogManager.GetLogger(typeof(Roi)); + public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } + + readonly RoiCfg roiCfg; + + /// + /// Camera and ICamera + /// + public readonly CLP1611.Camera NetCamera; + public GenericDevices.ICamera Camera { get { return NetCamera as GenericDevices.ICamera; } } + + public int Position + { + get + { + int firstDigitPos = Name.IndexOfAny(new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }); + int position; + return (firstDigitPos < 0) ? 0 : (int.TryParse(Name.Substring(firstDigitPos), out position) ? position : 0); + } + } + public RegisterReaderType RegisterReaderType { get { return RegisterReaderType.Manual; } } + public double PulsesPerLtr { get { return roiCfg.ProcParams.PulsesPerLtr; } } + public double LtrsPerPulse { get { return (PulsesPerLtr <= float.Epsilon) ? 1.0 : (1 / PulsesPerLtr); } } + + + double beginWMState; + public double BeginWMState + { + get { return beginWMState; } + set { beginWMState = value; } + } + + double endWMState; + public double EndWMState + { + get { return endWMState; } + set { endWMState = value; } + } + + public double WMVolume { get { return endWMState - beginWMState; } } + + public int WMPulses { get { return (int)(WMVolume / LtrsPerPulse); } } + + public int WMRefPulses { get { return StateMachine.ControlBoardMain.RefPulses; ; } } + + + public Roi() { } + + public Roi(Generic.IComponentCfg cfg, IList components) + : base(cfg) + { + roiCfg = cfg as RoiCfg; + NetCamera = TbfComponents.FindComponent(cfg.ParentName, components) as CLP1611.Camera; + log.Warn(this.ToString()); + } + + public override void Initialize() + { + Clear(); + } + + #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 CfgChangeHandler; + + public override void StartChangeHandler() + { + CfgChangeHandler += delegate(object sender, CfgChangeArgs args) + { + RoiCfg tmpcfg = args.Cfg as RoiCfg; + if (tmpcfg != null && tmpcfg.Name.Equals(Name)) + { + if (args.Command == CfgChangeCmd.CfgChange) + { + roiCfg.BlackAndWhite = tmpcfg.BlackAndWhite; + roiCfg.LowResolution = tmpcfg.LowResolution; + roiCfg.ImageRotation = tmpcfg.ImageRotation; + } + } + }; + } + + #endregion Configuration Change Handling + + public void Clear() + { + log.DebugFormat("{0}:Clear()", Name); + beginWMState = 0; + endWMState = 0; + } + + + public IOperation GrabImageOp(string imageFileName) + { + return GrabImageOp(imageFileName, roiCfg.BlackAndWhite, roiCfg.LowResolution, roiCfg.ImageRotation); + } + + public IOperation GrabImageOp(string imageFileName, + bool blackAndWhite, bool lowResolution, ImageRotation imageRotation) + { + if (NetCamera != null) + { + /// TODO: Implement support for sharing one camera by multiple ROI-s + return NetCamera.GrabImagesOp(new string[] { imageFileName }, blackAndWhite, lowResolution, imageRotation); + } + else + return null; + } + + public IValve GetValve() + { + return null; + } + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfg.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfg.cs new file mode 100644 index 000000000..ba6e10961 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfg.cs @@ -0,0 +1,56 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System.Collections.Generic; +using System.Xml.Serialization; +using Common; +using Config.Entities; +using TBF.Rig.Generic; + +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + public class RoiCfg : ComponentCfgBase, IChildComponentCfg + { + public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(RoiCfg) })[0]; + public override XmlSerializer GetSerializer() { return Serializer; } + + public IComponentCfgCtrl GetControl(IList cmpntEntities) { return new RoiCfgCtrl(); } + + /// + /// Serialized parameters + /// + public bool BlackAndWhite; + public bool LowResolution; + public ImageRotation ImageRotation; + + /// Procedure parameters + [XmlIgnore] + public ProcedureParams ProcParams; + public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; } + public override IParamsProvider CreateProcParamsProvider() { return new ProcedureParams(true); } + + /// Private parameterless constructor invoked by all other (public) constructors + RoiCfg() + { + ProcParams = new ProcedureParams(true); + } + + public RoiCfg(string name, IComponentFactory factory) + : this() + { + Name = name; + Factory = factory; + ParentName = "Camera"; + BlackAndWhite = false; + LowResolution = true; + ImageRotation = ImageRotation.None; + DebugLevel = DebugMode.Inherit; + } + + public string ToString(int i) + { + return string.Format("{0} Camera={1} B&W={2}, LowRes={3}, Rotation={4}", + Name, ParentName, BlackAndWhite, LowResolution, ImageRotation); + } + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.cs new file mode 100644 index 000000000..2b47af0f4 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.cs @@ -0,0 +1,175 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +using System; +using System.Windows.Forms; +using log4net; +using Common; +using Config.Entities; +using TBF.Rig.Generic; +using TBF.Resources; +using TBF.UI.Bench.Components; + +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + public partial class RoiCfgCtrl : UserControl, IComponentCfgCtrl + { + static readonly ILog log = LogManager.GetLogger(typeof(RoiCfgCtrl)); + + ComponentParametersDlg parent; + + public bool ShowMore { get { return false; } } + + RoiCfg config; + public IComponentCfg Config + { + get { return config as IComponentCfg; } + set + { + config = value as RoiCfg; + Redraw(); + } + } + + public RoiCfgCtrl() + { + InitializeComponent(); + } + + private void PumpCfgCtrl_Load(object sender, EventArgs e) + { + parent = ParentForm as ComponentParametersDlg; + if (parent == null) return; + + if (parent.CmpntEntities != null) + { + foreach (var cmpnt in parent.CmpntEntities) + { + if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is TBF.Rig.Network.Camera.CLP1611.Factory) + { + parentNameComboBox.Items.Add(cmpnt.Name); + } + } + } + + for (ImageRotation ir = 0; ir < ImageRotation.Count; ir++) + { + imageRotationComboBox.Items.Add(ir.ToDescription()); + } + + Redraw(); + } + + public void Closing() + { + } + + void Redraw() + { + if (config == null) return; /// Control was not loaded, settings were not changed + + classNameLabel.Text = config.Factory.ClassName; + nameTextBox.Text = config.Name; + parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName; + blackAndWhiteCheckBox.Checked = config.BlackAndWhite; + loResolutionCheckBox.Checked = config.LowResolution; + imageRotationComboBox.Text = config.ImageRotation.ToDescription(); + } + + public void Unlock() + { + nameTextBox.Enabled = true; + parentNameComboBox.Enabled = true; + blackAndWhiteCheckBox.Enabled = true; + loResolutionCheckBox.Enabled = true; + imageRotationComboBox.Enabled = true; + } + + public CfgUpdateFlags VerifyCfg(ref string message) + { + CfgUpdateFlags flags = CfgUpdateFlags.None; + + if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text)) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + string.Format(Strings.Invalid_0, parentNameLabel.Text); + } + + if (!imageRotationComboBox.Items.Contains(imageRotationComboBox.Text)) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + string.Format(Strings.Invalid_0, imageRotationLabel.Text); + } + + return flags; + } + + public CfgUpdateFlags UpdateCfg() + { + CfgUpdateFlags flags = CfgUpdateFlags.None; + + if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed + + if (config.Name != nameTextBox.Text) + { + config.Name = nameTextBox.Text; + flags |= (CfgUpdateFlags.RestartRqrd | CfgUpdateFlags.AnyChange); + } + + string newParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text; + if (config.ParentName != newParentName) + { + config.ParentName = newParentName; + flags |= (CfgUpdateFlags.RestartRqrd | CfgUpdateFlags.AnyChange); + } + + if (config.BlackAndWhite != blackAndWhiteCheckBox.Checked) + { + config.BlackAndWhite = blackAndWhiteCheckBox.Checked; + flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.InvokeCfgChange); + } + + if (config.LowResolution != loResolutionCheckBox.Checked) + { + config.LowResolution = loResolutionCheckBox.Checked; + flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.InvokeCfgChange); + } + + for (ImageRotation ir = 0; ir < ImageRotation.Count; ir++) + { + if (imageRotationComboBox.Text == ir.ToDescription()) + { + if (config.ImageRotation != ir) + { + config.ImageRotation = ir; + flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.InvokeCfgChange); + break; + } + } + } + + if ((flags & CfgUpdateFlags.InvokeCfgChange) != 0) + { + Roi.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.CfgChange, config)); + } + + return flags; + } + + #region Configuration Change Handling + + public static void OnCmdResponse(object sender, CmdResponseArgs args) + { + if (CmdResponseHandler == null) return; + try { CmdResponseHandler(sender, args); } + catch (Exception e) { log.Error("CmdResponseHandler(...) failed", e); } + } + + public static event EventHandler CmdResponseHandler; + + public void StartResponseHandler() { } + public void StopResponseHandler() { } + + #endregion Configuration Change Handling + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.designer.cs b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.designer.cs new file mode 100644 index 000000000..d0bb56573 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.designer.cs @@ -0,0 +1,185 @@ +/// +/// Copyright (c) 2017 Sensus Metering Systems +/// +namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11 +{ + partial class RoiCfgCtrl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + 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.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.imageRotationComboBox = new System.Windows.Forms.ComboBox(); + this.imageRotationLabel = new System.Windows.Forms.Label(); + this.blackAndWhiteCheckBox = new System.Windows.Forms.CheckBox(); + this.loResolutionCheckBox = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // parentNameLabel + // + this.parentNameLabel.AutoSize = true; + this.parentNameLabel.Location = new System.Drawing.Point(20, 77); + this.parentNameLabel.Name = "parentNameLabel"; + this.parentNameLabel.Size = new System.Drawing.Size(43, 13); + this.parentNameLabel.TabIndex = 3; + this.parentNameLabel.Text = "Camera"; + // + // nameTextBox + // + this.nameTextBox.Enabled = false; + this.nameTextBox.Location = new System.Drawing.Point(139, 49); + this.nameTextBox.Name = "nameTextBox"; + this.nameTextBox.Size = new System.Drawing.Size(174, 20); + this.nameTextBox.TabIndex = 2; + // + // nameLabel + // + this.nameLabel.AutoSize = true; + this.nameLabel.Location = new System.Drawing.Point(20, 52); + 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(136, 26); + this.classNameLabel.Name = "classNameLabel"; + this.classNameLabel.Size = new System.Drawing.Size(114, 13); + this.classNameLabel.TabIndex = 0; + this.classNameLabel.Text = "ComponentClassName"; + // + // parentNameComboBox + // + this.parentNameComboBox.Enabled = false; + this.parentNameComboBox.FormattingEnabled = true; + this.parentNameComboBox.Location = new System.Drawing.Point(139, 74); + this.parentNameComboBox.Name = "parentNameComboBox"; + this.parentNameComboBox.Size = new System.Drawing.Size(174, 21); + this.parentNameComboBox.TabIndex = 4; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(-211, -147); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(57, 13); + this.label1.TabIndex = 5; + this.label1.Text = "Arguments"; + // + // textBox1 + // + this.textBox1.Enabled = false; + this.textBox1.Location = new System.Drawing.Point(-125, -150); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(372, 20); + this.textBox1.TabIndex = 6; + // + // imageRotationComboBox + // + this.imageRotationComboBox.Enabled = false; + this.imageRotationComboBox.FormattingEnabled = true; + this.imageRotationComboBox.Location = new System.Drawing.Point(139, 154); + this.imageRotationComboBox.Name = "imageRotationComboBox"; + this.imageRotationComboBox.Size = new System.Drawing.Size(93, 21); + this.imageRotationComboBox.TabIndex = 8; + // + // imageRotationLabel + // + this.imageRotationLabel.AutoSize = true; + this.imageRotationLabel.Location = new System.Drawing.Point(20, 157); + this.imageRotationLabel.Name = "imageRotationLabel"; + this.imageRotationLabel.Size = new System.Drawing.Size(103, 13); + this.imageRotationLabel.TabIndex = 7; + this.imageRotationLabel.Text = "Image rotation (ccw)"; + // + // blackAndWhiteCheckBox + // + this.blackAndWhiteCheckBox.AutoSize = true; + this.blackAndWhiteCheckBox.Enabled = false; + this.blackAndWhiteCheckBox.Location = new System.Drawing.Point(139, 108); + this.blackAndWhiteCheckBox.Name = "blackAndWhiteCheckBox"; + this.blackAndWhiteCheckBox.Size = new System.Drawing.Size(93, 17); + this.blackAndWhiteCheckBox.TabIndex = 5; + this.blackAndWhiteCheckBox.Text = "Black && White"; + this.blackAndWhiteCheckBox.UseVisualStyleBackColor = true; + // + // loResolutionCheckBox + // + this.loResolutionCheckBox.AutoSize = true; + this.loResolutionCheckBox.Enabled = false; + this.loResolutionCheckBox.Location = new System.Drawing.Point(139, 131); + this.loResolutionCheckBox.Name = "loResolutionCheckBox"; + this.loResolutionCheckBox.Size = new System.Drawing.Size(94, 17); + this.loResolutionCheckBox.TabIndex = 6; + this.loResolutionCheckBox.Text = "Low resolution"; + this.loResolutionCheckBox.UseVisualStyleBackColor = true; + // + // RoiCfgCtrl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.imageRotationComboBox); + this.Controls.Add(this.imageRotationLabel); + this.Controls.Add(this.blackAndWhiteCheckBox); + this.Controls.Add(this.loResolutionCheckBox); + this.Controls.Add(this.parentNameComboBox); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.parentNameLabel); + this.Controls.Add(this.nameTextBox); + this.Controls.Add(this.nameLabel); + this.Controls.Add(this.classNameLabel); + this.Name = "RoiCfgCtrl"; + this.Size = new System.Drawing.Size(500, 300); + this.Load += new System.EventHandler(this.PumpCfgCtrl_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + 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.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.ComboBox imageRotationComboBox; + private System.Windows.Forms.Label imageRotationLabel; + private System.Windows.Forms.CheckBox blackAndWhiteCheckBox; + private System.Windows.Forms.CheckBox loResolutionCheckBox; + } +} diff --git a/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.resx b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/TBF/Rig/Network/Camera/RoiForFixedStartCJMS11/RoiCfgCtrl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TBF/Rig/TbfComponents.cs b/TBF/Rig/TbfComponents.cs index 4ef66b2d9..8e849cc9a 100644 --- a/TBF/Rig/TbfComponents.cs +++ b/TBF/Rig/TbfComponents.cs @@ -76,11 +76,13 @@ namespace TBF.Rig new Modbus.WaterAnalyzer.Factory(), new Network.Adapter.Factory(), new Network.AdapterFTP.Factory(), + new Network.Camera.CJMS11.Factory(), new Network.Camera.CLP1611.Factory(), new Network.Camera.KeyenceIV3G120.Factory(), new Network.Camera.Display.Factory(), new Network.Camera.Roi.Factory(), new Network.Camera.RoiForFixedStart.Factory(), + new Network.Camera.RoiForFixedStartCJMS11.Factory(), new Network.Camera.RoiForFixedStartKeyence.Factory(), new Network.Comet.Ambient.Factory(), new Network.RestAPI.Factory(), diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index 42eb13944..eb99e48c9 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -813,6 +813,33 @@ NetadapterCfgCtrl.cs + + + + UserControl + + + CameraCfgCtrl.cs + + + + + + + + + + + + + + + + Form + + + TerminalDlg.cs + @@ -861,6 +888,16 @@ + + + + + + UserControl + + + RoiCfgCtrl.cs + @@ -2891,6 +2928,12 @@ NetadapterCfgCtrl.cs + + CameraCfgCtrl.cs + + + TerminalDlg.cs + CameraCfgCtrl.cs @@ -2906,6 +2949,9 @@ CameraCfgCtrl.cs + + RoiCfgCtrl.cs + RoiCfgCtrl.cs diff --git a/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs b/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs new file mode 100644 index 000000000..538e20fed --- /dev/null +++ b/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using JetBrains.Annotations; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TBF.Rig.Generic; +using TBF.Rig.Network.Camera.CJMS11; + +namespace TBFTests.Rig.Network.Camera.CJMS11 +{ + [TestClass] + [TestSubject(typeof(TBF.Rig.Network.Camera.CJMS11.Camera))] + public class CameraTest + { + + [TestMethod] + public void Initialize() + { + Factory factory = new Factory(); + IComponentCfg config = new CameraCfg("Network.Adapter", factory); + + + TBF.Rig.Network.Camera.CJMS11.Camera camera = new TBF.Rig.Network.Camera.CJMS11.Camera(config, null); + + camera.Initialize(); + + Thread.Sleep(10000); + camera.Stop(); + Thread.Sleep(10000); + } + } +} \ No newline at end of file diff --git a/TBFTests/TBFTests.csproj b/TBFTests/TBFTests.csproj index 26fc94f62..13852062d 100644 --- a/TBFTests/TBFTests.csproj +++ b/TBFTests/TBFTests.csproj @@ -97,6 +97,7 @@ + diff --git a/TBFTests/obj/Debug/TBFTests.csproj.CoreCompileInputs.cache b/TBFTests/obj/Debug/TBFTests.csproj.CoreCompileInputs.cache index b955293cd..2b71a7c5e 100644 --- a/TBFTests/obj/Debug/TBFTests.csproj.CoreCompileInputs.cache +++ b/TBFTests/obj/Debug/TBFTests.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -42f452de9b0393e2b8a43441d6acf2f1268eaff5eb4a5cb56624e462418cf079 +8f46c126994e2e4deda67fad3f33cb2a280d2b4b7ce364598de6bc2d540d1738