티스토리 뷰

SCADA

웹기반 HMI ScadaBR의 구조 2

록개발자 2018. 11. 9. 11:48
SCADABR 시스템 구조를 파악하는 효과적인 방법중에 하나는 시스템의 빌드 과정을 분석하는 것으로 SCADABR이 JAVA 프로젝트 이고 MAVEN을 사용하고 있으므로 프로젝트의 pom.xml을 살펴보면 연관 라이브러리들을 확인할 수 있습니다.
아래의 표는 pom.xml에 기술되어 빌드에 포함되는 연관 패키지입니다. 이 라이브러리 들은 설치후 WEB-INF/lib폴더에 저장합니다.
 
  groupId artifactId version scope
1 axis axis 1.2.1 compile
2 br.org.scadabr dnp34j 1.12.4
3 br.org.scadabr.protocol iec101 1.12.4
4 com.atlassian crowd-integration-client 2.1.1
5 com.dalsemi onewire 1.10 compile
6 com.i2msolucoes alpha24j 1.12.4
7 com.serotonin spinwave 1.12.0 compile
8 com.serotonin serotonin-util 1.12.4 compile
9 com.serotonin modbus4J 1.12.0 compile
10 com.serotonin backnet4j 1.12.0
11 com.serotonin viconics 1.12.0
12 commons-codec commons-codec 1.4 compile
13 commons-dbcp commons-dbcp 1.4
14 commons-fileupload commons-fileupload 1.2.2 compile
15 commons-httpclient commons-httpclient 3.1 compile
16 commons-io commons-io 1.4 compile
17 commons-logging commons-logging 1.1.1
18 commons-pool commons-pool 1.5.5
19 javax.activation activation 1.1.1 compile
20 javax.mail mail 1.4.1 compile
21 javax.servlet servlet-api 2.5 provided
22 javax.servlet jstl 1.2 compile
23 javax.servlet.jsp jsp-api 2.1 provided
24 javax.xml jaxrpc-api 1.1
25 jfree jcommon 1.0.15 compile
26 jfree jfreechart 1.0.13 compile
27 joda-time joda-time 1.6.2 compile
28 mysql mysql-connector-java 5.1.13
29 net.sf.fhz4j fhz4j-core 0.1.4-SNAPSHOT compile
30 net.sf.mbus4j mbus4j-master 0.1.4-SNAPSHOT compile
31 net.sf.openv4j openv4j-core 0.1.4-SNAPSHOT compile
32 org.apache.derby derby 10.6.1.0 compile
33 org.apache.derby derbytools 10.6.1.0 compile
34 org.directwebremoting dwr 2.0.3
35 org.freemarker freemarker 2.3.16 compile
36 org.openscada opc-driver 0.5
37 org.openscada openscada-utils 0.5
38 org.openscada opc-dcom 0.5
39 org.openscada opc-lib 0.5
40 org.quartz-scheduler quartz 1.7.2
41 org.rxtx rxtx 2.2
42 org.snmp4j snmp4j 1.10.1 compile
43 org.springframework spring 2.5.6 compile
44 org.springframework spring-webmvc 2.5.6 compile
45 taglibs standard 1.1.2 compile
46 taglibs log 1.0 compile
 
  
연관 패키지를 살펴보면 다른 오픈소스 프로젝트로부터(openscada, mango, rxtx...) 드라이버 코드등을 가져왔음을 확인할 수 있고, 주의 깊게 확인할 것은 스프링 프레임워크를 적용하고 있다는 것입니다.

 
스프링 프레임워크에 대한 설명은 생략하고 각종 설정이 담긴 XML 파일을 기반으로 시스템의 구조를 파악해 나갑니다.
가장 핵심 역할을 하는 파일은 WEB-INF/web.xml 입니다.


web.xml의 주요 내용을 살펴보면 우선 <context-param>을 이용해서 서버에 전달할 기본 파라미터를 설정합니다.
필터는 클라이언트와 서버 사이에서 스트림의 검사나 보정 작업 등을 수행할 수 있는 것으로 <filter>로 필터를 정의하고 <filter-mapping>으로 특정 URL에 해당하는 필터를 맵핑시킵니다.
 
<filter>
<filter-name>IsLoggedIn</filter-name>
<filter-class>com.serotonin.mango.web.filter.NormalLoggedInFilter</filter-class>
<init-param>
<param-name>forwardUrl</param-name>
<param-value>/login.htm</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>IsLoggedIn</filter-name>
<url-pattern>*.shtm</url-pattern>
</filter-mapping>
 위의 코드 예제는 IsLoggedIn 필터를 정의하고 URL이 *.shtm으로 끝나는 것은 반드시 로그인 검사를 수행하고 비로그인 상태이면 /login.htm으로 이동시키도록 하는 설정입니다. 아래는 필터 역할을 수행하는 코드입니다.

abstract public class LoggedInFilter implements Filter {
    private final Log LOGGER = LogFactory.getLog(LoggedInFilter.class);


    private String forwardUrl;


    public void init(FilterConfig config) {
        forwardUrl = config.getInitParameter("forwardUrl");
    }


    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        // Assume an http request.
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;


        boolean loggedIn = true;


        User user = Common.getUser(request);
        if (!checkAccess(user))
            loggedIn = false;


        if (loggedIn && CrowdUtils.isCrowdEnabled()) {
            if (CrowdUtils.isCrowdAuthenticated(user))
                // The user may not have been authenticated by Crowd, so only check with Crowd if it was.
                loggedIn = CrowdUtils.isAuthenticated(request, response);
        }


        if (!loggedIn) {
            LOGGER.info("Denying access to secure page for session id " + request.getSession().getId() + ", uri="
                    + request.getRequestURI());
            response.sendRedirect(request.getContextPath() + forwardUrl);
            //request.getRequestDispatcher(forwardUrl).forward(request, response);
            return;
        }


        // Continue with the chain.
        filterChain.doFilter(servletRequest, servletResponse);
    }


    public void destroy() {
        // no op
    }


    abstract protected boolean checkAccess(User user);
}
web.xml의 또다른 핵심 설정은 서블릿 정의로 <servlet>로 서블릿의 이름과 클래스를 설정하고 특정 URL에 해당하는 서블릿 지정은 <servlet-mapping>으로 수행합니다. 다음의 코드는 springDispatcher를 정의하고 *.htm과 *.shtm을 springDispatcher로 맵핑시킨 예제입니다.
        <servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>


<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>*.shtm</url-pattern>
</servlet-mapping>
 
위 코드의 설정 처럼 *.htm, *.shtm으로 끝나는 URL은 실제로는 단순한 HTML 파일이 아닙니다. 스프링 프레임워크의 처리 흐름에 따라 springDispatcher로 전달되고 springDispatcher는 springDispatcher-servlet.xml에 지정한 컨트롤러로 연결합니다.
        <prop key="/compound_events.shtm">compoundEventsController</prop>
        <prop key="/data_point_details.shtm">dataPointDetailsController</prop>
        <prop key="/data_point_edit.shtm">dataPointEditController</prop>
        <prop key="/data_source_edit.shtm">dataSourceEditController</prop>
        <prop key="/data_sources.shtm">dataSourceListController</prop>
        <prop key="/emport.shtm">emportController</prop>
        <prop key="/event_handlers.shtm">eventHandlersController</prop>
        <prop key="/events.shtm">eventsController</prop>
        <prop key="/help.shtm">helpController</prop>
        <prop key="/login.htm">loginController</prop>
        <prop key="/logout.htm">logoutController</prop>
        <prop key="/mailing_lists.shtm">mailingListsController</prop>
        <prop key="/maintenance_events.shtm">maintenanceEventsController</prop>
        <prop key="/point_hierarchy.shtm">pointHierarchyController</prop>
        <prop key="/point_links.shtm">pointLinksController</prop>
        <prop key="/public_view.htm">publicViewController</prop>
        <prop key="/publisher_edit.shtm">publisherEditController</prop>
        <prop key="/publishers.shtm">publisherListController</prop>
        <prop key="/reports.shtm">reportsController</prop>
        <prop key="/reportChart.shtm">reportChartController</prop>
        <prop key="/scheduled_events.shtm">scheduledEventsController</prop>
        <prop key="/scripting.shtm">scriptingController</prop>
        <prop key="/sql.shtm">sqlController</prop>
        <prop key="/system_settings.shtm">systemSettingsController</prop>
        <prop key="/users.shtm">usersController</prop>
        <prop key="/views.shtm">viewsController</prop>
        <prop key="/view_edit.shtm">viewEditController</prop>
        <prop key="/watch_list.shtm">watchListController</prop>
        <prop key="/webcam_live_feed.htm">webcamLiveFeedController</prop>
        <prop key="/export_project.htm">projectExporterController</prop>
        <prop key="/import_project.htm">projectImporterController</prop>
       
        <!-- Mobile user URLs -->
        <prop key="/mobile_login.htm">mobileLoginController</prop>
        <prop key="/mobile_logout.htm">mobileLogoutController</prop>
        <prop key="/mobile_watch_list.shtm">mobileWatchListController</prop>
......
  <bean id="loginController" class="com.serotonin.mango.web.mvc.controller.LoginController">
    <property name="commandName"><value>login</value></property>
    <property name="commandClass"><value>com.serotonin.mango.web.mvc.form.LoginForm</value></property>
    <property name="formView"><value>login</value></property>
    <property name="successUrl"><value>watch_list.shtm</value></property>
    <property name="newUserUrl"><value>help.shtm</value></property>
    <property name="bindOnNewForm"><value>true</value></property>
  </bean>
 
  <bean id="logoutController" class="com.serotonin.mango.web.mvc.controller.LogoutController">

    <property name="redirectUrl"><value>login.htm</value></property>
  </bean>
 
  <bean id="mobileLoginController" class="com.serotonin.mango.web.mvc.controller.LoginController">

    <property name="commandName"><value>login</value></property>
    <property name="commandClass"><value>com.serotonin.mango.web.mvc.form.LoginForm</value></property>
    <property name="formView"><value>mobile/login</value></property>
    <property name="mobile"><value>true</value></property>
    <property name="successUrl"><value>mobile_watch_list.shtm</value></property>
    <property name="bindOnNewForm"><value>true</value></property>
  </bean>
 
위의 설정 예제에 있는 /login.htm 주소는 login.htm 파일을 찾아 보여주는 것이 아니라 loginController로 컨트롤이 전달됩니다. loginController 실제 클래스는 <bean>을 통해서 정의됩니다. 다음은 로그인을 처리하는 컨트롤러 코드의 일부입니다.
private ModelAndView performLogin(HttpServletRequest request, String username) {
        // Check if the user is already logged in.
        User user = Common.getUser(request);
        if (user != null && user.getUsername().equals(username)) {
            // The user is already logged in. Nothing to do.
            if (logger.isDebugEnabled())
                logger.debug("User is already logged in, not relogging in");
        }
        else {
            UserDao userDao = new UserDao();
            // Get the user data from the app server.
            user = new UserDao().getUser(username);


            // Update the last login time.
            userDao.recordLogin(user.getId());


            // Add the user object to the session. This indicates to the rest
            // of the application whether the user is logged in or not.
            Common.setUser(request, user);
            if (logger.isDebugEnabled())
                logger.debug("User object added to session");
        }


        if (!mobile) {
            if (user.isFirstLogin())
                return new ModelAndView(new RedirectView(newUserUrl));
            if (!StringUtils.isEmpty(user.getHomeUrl()))
                return new ModelAndView(new RedirectView(user.getHomeUrl()));
        }


        return new ModelAndView(new RedirectView(successUrl));
    }
 
위에서 언급한 내부 처리과정은 서블릿으로 처리하고 스프링 응용의 전형적인 사용 방법처럼 SCADABR이 사용자 인터페이스를 보여주는 작업은 JSP 코드를 통해서 처리합니다. 다음은 WEB-INF/jsp 폴더의 JSP 파일 목록입니다.
│  compoundEvents.jsp
│  dataPointDetails.jsp
│  dataPointEdit.jsp
│  dataSourceEdit.jsp
│  dataSourceList.jsp
│  emport.jsp
│  eventHandlers.jsp
│  events.jsp
│  help.jsp
│  import_result.jsp
│  login.jsp
│  mailingLists.jsp
│  maintenanceEvents.jsp
│  pointHierarchy.jsp
│  pointLinks.jsp
│  publicView.jsp
│  publisherEdit.jsp
│  publisherList.jsp
│  reports.jsp
│  scheduledEvents.jsp
│  scripting.jsp
│  sql.jsp
│  systemSettings.jsp
│  users.jsp
│  viewEdit.jsp
│  views.jsp
│  watchList.jsp
│  webcamLiveFeed.jsp
│  
├─dataSourceEdit
│      dsEventsFoot.jspf
│      dsFoot.jspf
│      dsHead.jspf
│      editAlpha2.jsp
│      editAsciiFile.jsp
│      editAsciiSerial.jsp
│      editBacnetIp.jsp
│      editDnp3.jsp
│      editDnp3Ip.jsp
│      editDnp3Serial.jsp
│      editDrStorageHt5b.jsp
│      editEBI25.jsp
│      editFhz4J.jsp
│      editGalil.jsp
│      editHttpImage.jsp
│      editHttpReceiver.jsp
│      editHttpRetriever.jsp
│      editIEC101.jsp
│      editIEC101Ethernet.jsp
│      editIEC101Serial.jsp
│      editInternal.jsp
│      editJmx.jsp
│      editMBus.jsp
│      editMeta.jsp
│      editModbus.jsp
│      editModbusIp.jsp
│      editModbusSerial.jsp
│      editNmea.jsp
│      editNodaveS7.jsp
│      editOneWire.jsp
│      editOpc.jsp
│      editOpenV4J.jsp
│      editPachube.jsp
│      editPersistent.jsp
│      editPop3.jsp
│      editRadiuino.jsp
│      editSerialSettings.jsp
│      editSnmp.jsp
│      editSpinwave.jsp
│      editSql.jsp
│      editViconics.jsp
│      editVirtual.jsp
│      editVMStat.jsp
│      
├─include
│      compoundEditor.jsp
│      customEditor.jsp
│      graphicRendererEditor.jsp
│      settingsEditor.jsp
│      staticEditor.jsp
│      tech.jsp
│      userComment.jsp
│      
├─mobile
│      login.jsp
│      watchList.jsp
│      
├─pointEdit
│      buttons.jsp
│      chartRenderer.jsp
│      eventDetectors.jsp
│      loggingProperties.jsp
│      pointName.jsp
│      pointProperties.jsp
│      textRenderer.jsp
│      valuePurge.jsp
│      
└─publisherEdit
        editHttpSender.jsp
        editPachube.jsp
        editPersistent.jsp

 
사용자가 웹 화면을 통해서 로그인하고 결과 화면을 받아보는 과정을 web.xml, JSP, 서블릿등을 통해서 알아보았는데, SCADABR의 WatchList 화면 등의 경우처럼 서버의 데이터를 출력하고 갱신해야 하는 경우 SCADABR에서는 AJAX와 유사한 기술인 DWR(Direct Web Remote)을 사용한다. 앞서 web.xml에서는 dwr-invoker라는 서블릿 정의가 있었고, applicationContext.xml에는 다음과 같이 DWR 단위의 클래스를 정의합니다.
  <bean id="CompoundEventsDwr" class="com.serotonin.mango.web.dwr.CompoundEventsDwr"/>
  <bean id="CustomViewDwr" class="com.serotonin.mango.web.dwr.CustomViewDwr"/>
  <bean id="DataPointDetailsDwr" class="com.serotonin.mango.web.dwr.DataPointDetailsDwr"/>
  <bean id="DataPointEditDwr" class="com.serotonin.mango.web.dwr.DataPointEditDwr"/>
  <bean id="DataSourceEditDwr" class="com.serotonin.mango.web.dwr.DataSourceEditDwr"/>
  <bean id="DataSourceListDwr" class="com.serotonin.mango.web.dwr.DataSourceListDwr"/>
  <bean id="EmportDwr" class="com.serotonin.mango.web.dwr.EmportDwr"/>
  <bean id="EventHandlersDwr" class="com.serotonin.mango.web.dwr.EventHandlersDwr"/>
  <bean id="EventsDwr" class="com.serotonin.mango.web.dwr.EventsDwr"/>
  <bean id="MailingListsDwr" class="com.serotonin.mango.web.dwr.MailingListsDwr"/>
  <bean id="MaintenanceEventsDwr" class="com.serotonin.mango.web.dwr.MaintenanceEventsDwr"/>
  <bean id="MiscDwr" class="com.serotonin.mango.web.dwr.MiscDwr"/>
  <bean id="PointHierarchyDwr" class="com.serotonin.mango.web.dwr.PointHierarchyDwr"/>
  <bean id="PointLinksDwr" class="com.serotonin.mango.web.dwr.PointLinksDwr"/>
  <bean id="PublisherEditDwr" class="com.serotonin.mango.web.dwr.PublisherEditDwr"/>
  <bean id="PublisherListDwr" class="com.serotonin.mango.web.dwr.PublisherListDwr"/>
  <bean id="ReportsDwr" class="com.serotonin.mango.web.dwr.ReportsDwr"/>
  <bean id="ScheduledEventsDwr" class="com.serotonin.mango.web.dwr.ScheduledEventsDwr"/>
  <bean id="SystemSettingsDwr" class="com.serotonin.mango.web.dwr.SystemSettingsDwr"/>
  <bean id="UsersDwr" class="com.serotonin.mango.web.dwr.UsersDwr"/>
  <bean id="ViewDwr" class="com.serotonin.mango.web.dwr.ViewDwr"/>
  <bean id="WatchListDwr" class="com.serotonin.mango.web.dwr.WatchListDwr"/>
  <bean id="ScriptsDwr" class="br.org.scadabr.web.dwr.ScriptsDwr"/>

 
하위의 JavaScript와의 DWR간의 연관성은 dwr.xml에 정의합니다. 다음 그림은 이클립스에서 dwr.xml을 편집하는 모습입니다.




(주)동운시스템 전화 041-358-3760

동운 HMI 소개 바로가기
[온라인 문의 및 견적요청]

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함