package utils; import com.ruoyi.code.camera.domain.Camera; import com.ruoyi.code.camera.service.ICameraService; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.utils.BeanUtil; import com.ruoyi.common.utils.StringUtils; import com.sun.jna.Native; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.ptr.IntByReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import sdk.java.common.GlobalTool; import sdk.java.lib.DataUtils; import sdk.java.lib.netmanager.NetDelegates; import sdk.java.lib.netmanager.NetEnums; import sdk.java.lib.netmanager.NetEnums.EM_LOGIN_PROTOCAL_TYPE; import sdk.java.lib.netmanager.NetEnums.EM_REALPLAY_STREAM_TYPE; import sdk.java.lib.netmanager.NetLib; import sdk.java.lib.netmanager.NetStructs; import sdk.java.lib.playmanager.PlayDefs; import sdk.java.lib.playmanager.PlayEnums.ENUM_RENDER_TYPE; import sdk.java.lib.playmanager.PlayLib; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import java.util.*; // 主动注册流程概要 ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * * 1、服务端开启主动注册监听服务(即此demo); * 2、设备端网络设置中,设置服务端IP和端口号,并使能主动注册;(步骤1、2没有固定的先后顺序); * 3、服务端等待设备上送IP、端口号和设备SN; * 4、服务端收到设备上送信息后,根据上送信息登陆设备,建立链接; * 5、登陆成功后,正常使用其余接口; * 6、接口调用。。。; * 7、断开连接的方式有以下两种,需注意两种方式都需要服务端主动登出设备: * 1)。设备端停止主动注册,服务端进入断线回调,并在回调中登出设备; * 2)。服务端停止注册服务,并登出所有设备; * * 注:此demo中的设备用户名和密码默认为 admin 和 admin123,以便登陆时使用,实际使用中,需自行管理每台设备的用户名和密码 * */ ///////////////////////////////////////////////////////////////////////////////////////////////////// @Component public class RegisterUtil implements ActionListener { private String m_jtfServerIP = "172.16.46.58"; // private String m_jtfServerIP = "192.168.1.11"; private String m_jtfServerPort = "8020"; private String m_jtfDevUsername = "admin"; private String m_jtfDevPassword = "admin123"; public String getUserName() {return m_jtfDevUsername;} public String getPassword() {return m_jtfDevPassword;} public JFrame m_frame; private JButton m_btnStopServer; private JButton m_btnStartServer; private JButton m_btnStopPlay; private JButton m_btnStartPlay; private JLabel m_lblLoadPic; private JCheckBox m_chkRealLoad; private JCheckBox m_chkIntelliDraw; private Panel m_panelVideo; ///////////////////////////////////////////////////////////////////////////////////////////////////// private JList m_listInfo; private JScrollPane m_jspListInfo; private Vector m_listInfoData = new Vector(); ///////////////////////////////////////////////////////////////////////////////////////////////////// private int m_nCurSelDeviceIndex = -1; private List m_listDevices = new ArrayList<>(); // 界面显示的设备列表 private List m_listDevicesData = new ArrayList<>(); // 用于存放所有主动注册成功的设备的登陆ID,作为设备的唯一标识 public List m_listLoginIDs = new ArrayList<>(); public NativeLong getCurDeviceLoginID() { if(-1 == this.m_nCurSelDeviceIndex) return new NativeLong(0); return m_listLoginIDs.get(this.m_nCurSelDeviceIndex); } public void setCurDeviceLoginID(NativeLong lLoginID) { if(-1 == this.m_nCurSelDeviceIndex) return; m_listLoginIDs.set(this.m_nCurSelDeviceIndex, lLoginID); } ///////////////////////////////////////////////////////////////////////////////////////////////////// public boolean bExit = true; private NativeLong m_lRegServerHandle = new NativeLong(0); // 主动注册服务句柄 private NativeLong m_lPlayHandle = new NativeLong(0); // 当前设备的播放句柄 private NativeLong m_lRealloadHandle = new NativeLong(0); // 当前设备的订阅句柄 private IntByReference m_nPlayPort = new IntByReference(0); // 当前设备的播放端口 private String m_strFilePath = "./TmpAutoReg"; // 将类对象存在map中,方便在回调中使用用户自定义变量作为key来获取类对象 private static Map s_mapFlagToClass = new HashMap(); ///////////////////////////////////////////////////////////////////////////////////////////////////// /** * Create the application. */ public RegisterUtil() { initialize(); initilizeRyNetLib(); } protected void finalize() { destroyRyNetLib(); } /** * Initialize the contents of the frame. */ private void initialize() { m_frame = new JFrame(); m_frame.setTitle("查看实时视频"); m_frame.setSize(1000,800) ; Toolkit toolkit = Toolkit.getDefaultToolkit(); int x = (int)(toolkit.getScreenSize().getWidth()-m_frame.getWidth())/2; int y = (int)(toolkit.getScreenSize().getHeight()-m_frame.getHeight())/2; m_frame.setLocation(x, y); // m_frame.setBounds(100, 100, 1400, 1100); m_frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); m_frame.getContentPane().setLayout(null); m_panelVideo = new Panel(); m_panelVideo.setBounds(0, 0, 1000, 800); m_panelVideo.setBackground(Color.BLACK); m_frame.getContentPane().add(m_panelVideo); } public boolean showVideo(NativeLong loginID){ if(0 == loginID.longValue()) { System.out.println("还未登录!"); return false; } Pointer ptrWnd = Native.getComponentPointer(this.m_panelVideo); boolean bResult = PlayLib.instance.PlayMS_GetFreePort(m_nPlayPort); if(!bResult) { System.out.println("设备繁忙,不能网络播放!"); return false; } // 设置实时流模式 PlayLib.instance.PlayMS_SetLogOption(7); bResult = PlayLib.instance.PlayMS_SetStreamOpenMode(m_nPlayPort.getValue(), PlayDefs.STREAM_TYPE_REALTIME); // 打开流,并初始化缓存池(大小 MIN_SOURCE_BUF_SIZE - MAX_SOURCE_BUF_SIZE) bResult &= PlayLib.instance.PlayMS_OpenStream(m_nPlayPort.getValue(), 1024 * 1024 * 20); // 在窗口 hwnd 播放流数据 bResult &= PlayLib.instance.PlayMS_Play(m_nPlayPort.getValue(), ptrWnd); if(!bResult) { System.out.println("播放视频失败!"); return false; } int channel = 0; // 目前通道号常用为 0 int nType = EM_REALPLAY_STREAM_TYPE.REALPLAY_STREAM_TYPE_REAL; // 实时播放流 this.m_lPlayHandle = NetLib.instance.Net_RealPlay(loginID, channel, null, nType); if(0 == this.m_lPlayHandle.longValue()) { JOptionPane.showMessageDialog(null, "实时播放失败!", "提示", JOptionPane.ERROR_MESSAGE); return false; } NetLib.instance.Net_SetRealDataCallBack(this.m_lPlayHandle, _realPlayDataCallBack, new NativeLong(this.m_nPlayPort.getValue())); System.out.println("播放实时视频成功."); return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * 设备断开连接后后,需要服务端主动登出设备,以保证下次能正常登陆 * */ public class DisConnectCallBack implements NetDelegates.fDisConnectCallBack { @Override public void invoke(NativeLong loginID, String ip, int port, NativeLong userdata) { int nFlag = (int) userdata.longValue(); if(1 == nFlag) { s_mapFlagToClass.get(nFlag).appendNotifyInfo(String.format("设备已断连!")); s_mapFlagToClass.get(nFlag).logout(loginID); s_mapFlagToClass.get(nFlag).removeDevice(loginID, ip, port); } } } DisConnectCallBack _cbDisConnectCallBack = new DisConnectCallBack(); ///////////////////////////////////////////////////////////////////////////////////////////////////// // 设备登陆信息 public static class STU_LOGIN_INFO extends Structure { public String strIP; public int nPort; public String strUserName; public String strPassword; public String strDevSN; protected List getFieldOrder() { return Arrays.asList(new String [] { "strIP", "nPort", "strUserName", "strPassword", "strDevSN"}); } public static class ByReference extends STU_LOGIN_INFO implements Structure.ByReference { } public static class ByValue extends STU_LOGIN_INFO implements Structure.ByValue { } }; /* * 用于存放登陆信息的列表 */ public Vector m_listLoginInfos = new Vector(); /* * 开启服务后,设备会主动建立连接,并上送自己的IP、端口和SN号(ptrParam)以便登陆使用。 * 支持多设备登陆 * */ public class RegServerCallBack implements NetDelegates.fRegServerCallBack { @Override public void invoke(NativeLong handle, String ip, int port, int command, Pointer ptrParam, int paramLen, NativeLong userdata) { // 通过userData获取类对象,userData为调用Net_RegStartServer时传入的值 int nFlag = (int) userdata.longValue(); if(1 == nFlag) { /* * 1、此处需判断指令,只有为1时,才执行登陆 * 2、第一个参数handle不是开启注册服务的handle,而是每台设备的链接句柄; * */ if(1 == command) { STU_LOGIN_INFO loginInfo = new STU_LOGIN_INFO(); loginInfo.strIP = ip; loginInfo.nPort = port; loginInfo.strUserName = s_mapFlagToClass.get(nFlag).getUserName(); loginInfo.strPassword = s_mapFlagToClass.get(nFlag).getPassword(); loginInfo.strDevSN = DataUtils.sdk_data_ptrToString(ptrParam, 0, paramLen, null); // 保存登陆信息,并在单开的线程中实现登陆 s_mapFlagToClass.get(nFlag).m_listLoginInfos.addElement(loginInfo); } } } } RegServerCallBack _cbRegServerCallBack = new RegServerCallBack(); ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * 登陆监听 * 此处通过线程来实现登陆,是因为我们不建议用户在sdk接口的回调中,直接再去调用其他sdk的接口,而是通过另单开一个线程来接受数据,并执行登陆 */ public class LoginListenThread extends Thread { private RegisterUtil m_runDevAutoReg; public LoginListenThread(RegisterUtil runDevAutoReg) { m_runDevAutoReg = runDevAutoReg; } @Override public void run() { while(!m_runDevAutoReg.bExit) { if(m_runDevAutoReg.m_listLoginInfos.size() > 0) { STU_LOGIN_INFO loginInfo = m_runDevAutoReg.m_listLoginInfos.get(0); System.out.println(loginInfo.strIP); System.out.println(loginInfo.nPort); System.out.println(loginInfo.strDevSN); // 登陆 m_runDevAutoReg.login(loginInfo.strIP, loginInfo.nPort, loginInfo.strDevSN); m_runDevAutoReg.m_listLoginInfos.removeElementAt(0); } } } public void startLoginListenThread() { m_runDevAutoReg.bExit = false; this.start(); } public void stopLoginListenThread() { m_runDevAutoReg.bExit = true; } } private LoginListenThread m_loginListenThread = null; ///////////////////////////////////////////////////////////////////////////////////////////////////// // 播放回调 private class RealPlayDataCallBack implements NetDelegates.fRealPlayDataCallBack { @Override public void invoke(NativeLong realHandle, int dataType, Pointer ptrBuffer, int bufferSize, NativeLong userdata) { if(0 != userdata.longValue()) { int nPort = (int) userdata.longValue(); // 将码流数据传给play库来播放 PlayLib.instance.PlayMS_InputData(nPort, ptrBuffer, bufferSize); } } } RealPlayDataCallBack _realPlayDataCallBack = new RealPlayDataCallBack(); ///////////////////////////////////////////////////////////////////////////////////////////////////// // 解析事件信息 private Object ParseEventInfoStructure(int eventType, Pointer ptrEventInfo) { if(null == ptrEventInfo) { System.out.printf("Parse event info [type:%d], null.\n", eventType); return null; } Object obj = null; ////////////////////////////////////////////////////////////////////// if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_SNAP_WHOLE) { NetStructs.STU_EVENT_FACE_SNAP_WHOLE stu = new NetStructs.STU_EVENT_FACE_SNAP_WHOLE(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_SNAP_PART) { NetStructs.STU_EVENT_FACE_SNAP_PART stu = new NetStructs.STU_EVENT_FACE_SNAP_PART(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_RECOGNIZE_WHOLE) { NetStructs.STU_EVENT_FACE_RECOGNIZE_WHOLE stu = new NetStructs.STU_EVENT_FACE_RECOGNIZE_WHOLE(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_RECOGNIZE_CUTOUT) { NetStructs.STU_EVENT_FACE_RECOGNIZE_CUTOUT stu = new NetStructs.STU_EVENT_FACE_RECOGNIZE_CUTOUT(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } ////////////////////////////////////////////////////////////////////// else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_TRAFFIC_PASS) { NetStructs.STU_EVENT_TRAFFIC_PASS stu = new NetStructs.STU_EVENT_TRAFFIC_PASS(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_TRAFFIC_PASS_CUTOUT) { NetStructs.STU_EVENT_TRAFFIC_PASS_CUTOUT stu = new NetStructs.STU_EVENT_TRAFFIC_PASS_CUTOUT(); DataUtils.sdk_data_ptrToStructure(ptrEventInfo, 0, stu); String strName = new String(stu.szEvent).trim(); System.out.printf("Event info [type:%d, name:%s].\n", eventType, strName); obj = stu; // ....................................... } else { System.out.printf("Event[type:%d], not supported.\n", eventType); return null; } return obj; } // 保存事件数据到本地(事件数据即图片数据) private boolean SaveBufferToLocal(int eventType, Object eventInfoObj, Pointer ptrBuffer, int bufferSize) { System.out.printf("Save buffer, Event:%d.\n", eventType); if(null == eventInfoObj || null == ptrBuffer || 0 >= bufferSize) { System.out.printf("Illegal params, Event:%d.\n", eventType); return false; } String filename = null; // 人脸检测背景图 if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_SNAP_WHOLE) { NetStructs.STU_EVENT_FACE_SNAP_WHOLE info = (NetStructs.STU_EVENT_FACE_SNAP_WHOLE)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } // 人脸检测抠图 else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_SNAP_PART) { NetStructs.STU_EVENT_FACE_SNAP_PART info = (NetStructs.STU_EVENT_FACE_SNAP_PART)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } // 人脸识别背景图 else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_RECOGNIZE_WHOLE) { NetStructs.STU_EVENT_FACE_RECOGNIZE_WHOLE info = (NetStructs.STU_EVENT_FACE_RECOGNIZE_WHOLE)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } // 人脸识别抠图 else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_FACE_RECOGNIZE_CUTOUT) { NetStructs.STU_EVENT_FACE_RECOGNIZE_CUTOUT info = (NetStructs.STU_EVENT_FACE_RECOGNIZE_CUTOUT)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } ////////////////////////////////////// // 交通卡口背景图 else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_TRAFFIC_PASS) { NetStructs.STU_EVENT_TRAFFIC_PASS info = (NetStructs.STU_EVENT_TRAFFIC_PASS)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } // 交通卡口抠图 else if(eventType == NetEnums.EM_EVENT_SP_TYPE.EVENT_SP_TRAFFIC_PASS_CUTOUT) { NetStructs.STU_EVENT_TRAFFIC_PASS_CUTOUT info = (NetStructs.STU_EVENT_TRAFFIC_PASS_CUTOUT)eventInfoObj; String strName = new String(info.szEvent).trim(); filename = String.format("%s\\%s_%04d%02d%02d_%02d%02d%02d.jpg", this.m_strFilePath, strName, info.stuTime.nYear, info.stuTime.nMonth, info.stuTime.nDay, info.stuTime.nHour, info.stuTime.nMinute, info.stuTime.nSecond); } if(null == filename || filename.isEmpty()) { System.out.printf("Get file name fail, Event:%d.\n", eventType); return false; } if(!DataUtils.sdk_data_fileWrite(ptrBuffer, 0, 0, bufferSize, filename)) { System.out.printf("Save file[%s] fail, Event:%d.\n", filename, eventType); return false; } refreshImage(filename); return true; } // 智能分析数据回调,图片订阅事件上报时触发 private class AnalyzeDataFunc implements NetDelegates.fAnalyzeDataCallBack { @Override public void invoke(NativeLong analyzeHandle, int eventType, Pointer ptrEventInfo, Pointer ptrBuffer, int bufferSize, NativeLong userdata, int sequence, Pointer ptrReserved) { System.out.printf("Handle:%d, Event:%d.\n", analyzeHandle.intValue(), eventType); // 解析事件信息 Object eventInfoObj = ParseEventInfoStructure(eventType, ptrEventInfo); // 保存事件数据 // 建议新建一个事件信息和图片数据(拷贝)处理队列,另起线程处理; // 建议不要在回调函数中进行影响性能的操作,这里作为演示 SaveBufferToLocal(eventType, eventInfoObj, ptrBuffer, bufferSize); /*......*/ } } private AnalyzeDataFunc _analyzeDataFunc = new AnalyzeDataFunc(); ///////////////////////////////////////////////////////////////////////////////////////////////////// private void initilizeRyNetLib() { s_mapFlagToClass.put(1, this); // 用户可以自定义 fDisConnectCB NetLib.instance.Net_Init(_cbDisConnectCallBack, new NativeLong(1)); NetLib.instance.Net_SetLogFolder("D:\\soft\\control\\TmpAutoReg"); NetLib.instance.Net_SetLogOption(7); // GlobalTool.createDirectory(this.m_strFilePath); } private void destroyRyNetLib() { NetLib.instance.Net_Exit(); } void refreshEnables() { // boolean bStartServer = this.m_lRegServerHandle.longValue() != 0; // boolean bLogin = getCurDeviceLoginID().longValue() != 0; // boolean bPlay = this.m_lPlayHandle.longValue() != 0; // boolean bRealload = this.m_lRealloadHandle.longValue() != 0; // // this.m_btnStartServer.setEnabled(!bStartServer); // this.m_btnStopServer.setEnabled(bStartServer); // // this.m_btnStartPlay.setEnabled(bLogin && !bPlay); // this.m_btnStopPlay.setEnabled(bLogin && bPlay); // // this.m_chkRealLoad.setEnabled(bLogin); // this.m_chkRealLoad.setSelected(bRealload); // // this.m_chkIntelliDraw.setEnabled(bLogin && bPlay); } ///////////////////////////////////////////////////////////////////////////////////////////////////// // 提示信息 public void appendNotifyInfo(String info) { // this.m_listInfoData.addElement(info); // this.m_listInfo.setListData(m_listInfoData); // this.m_listInfo.setSelectedIndex(m_listInfoData.size() - 1); // // Point p = new Point(); // p.setLocation(0, this.m_listInfo.getHeight()); // this.m_jspListInfo.getViewport().setViewPosition(p); } public boolean login(String ip, int port, String strDevSN) { ICameraService cameraService = BeanUtil.getBean(ICameraService.class); String strUserName; strUserName = this.m_jtfDevUsername; // 默认admin String strUserPassword; strUserPassword = this.m_jtfDevPassword; // 默认admin123 IntByReference nErr = new IntByReference(0); // 此接口专为 主动注册登陆 接口,与 Net_LoginDevice 有区别 NativeLong lLoginID = NetLib.instance.Net_LoginDeviceSpec( ip, port, strUserName, strUserPassword, EM_LOGIN_PROTOCAL_TYPE.EM_LOGIN_PROTOCAL_TYPE_REG_SERVER, strDevSN, null, nErr); if(0 != lLoginID.longValue()) { // ip port strUserName strUserPassword strDevSN this.m_listLoginIDs.add(lLoginID); this.m_listDevicesData.add(String.format("%s_%d_%s", ip, port, strDevSN)); this.m_listDevices.addAll(this.m_listDevicesData); cameraService.online(ip,port,strUserName,strUserPassword,strDevSN,lLoginID.toString()); int error = NetLib.instance.Net_LastError(); System.out.println("errorCode = "+ error); System.out.println(String.format("登陆成功, 设备ip = %s, port = %d, SN = %s.", ip, port, strDevSN)); refreshEnables(); } else { int nErrCode = NetLib.instance.Net_LastError(); System.out.printf(String.format("登陆失败, ip = %s, port = %d, SN = %s, errorCode = %d.", ip, port, strDevSN, nErrCode)); } System.out.println("======================================="); System.out.println("device list : "+m_listDevicesData.toString()); System.out.println("======================================="); return true; } public void logoutAll() { for(int i=0; i getLoginIDs(){ List list = this.m_listLoginIDs; return list; } }