<? 
/*********************************************************************
 *
 *  Wattmon
 *    
 *  File : iec104.cgi
 * 
 *  Description: Edit iec104 devices
 *
 *********************************************************************
 * Company:         Cynergy Software
 *
 * Software License Agreement
 *
 * Copyright (c) 2014-25 Cynergy Software.  All rights reserved.
 *
 * Cynergy licenses to you the right to use, modify, copy, and 
 * distribute: 
 * (i)  the Software when used on a Wattmon device
 * (ii) the Software on any platform or device for personal use
 *
 * For commercial use please contact us via http://www.wattmon.com
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
 * CYNERGY BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Akash Heimlich       26/06/25    v1.0
 ********************************************************************/
include("/app/config.inc");
$angular_controller="IEC104Ctrl"; 
$title="IEC104 Settings";
include("/app/header.inc");
?>
<?
  $iec104_ini = ini_get_array("/config/iec104.ini","config");
?>
<!-- BREADCRUMBS -->
<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item"><a href="/"><? print(lang('HOME','settings')) ?></a></li>
    <li class="breadcrumb-item"><a href="/app/settings.cgi"><? print(lang('CONTROL_PANEL','settings')) ?></a></li>
    <li class="breadcrumb-item active" aria-current="page">IEC104</li>
  </ol>
</nav>

<!-- NAVBAR -->
<h4><i class="icon-cog"></i>&nbsp;IEC104 Settings</h4>
<ul class="nav justify-content-end">
  <li class="nav-item">
    <a class="nav-link active" data-toggle="collapse" href="#collapseHelp" role="button" aria-expanded="false" aria-controls="collapseExample">
    <? print(lang('HELP','roleedit')) ?>
  </a>
  </li>
  <li class="nav-item">
    <a class="nav-link" onclick="document.location='/app/roles.cgi';"><i class="icon-remove"></i> <? print(lang('CLOSE','devicetypeedit')) ?></a>
  </li>
  <li ng-hide='settings_locked' class="nav-item">
    <a class="nav-link {{getClass()}}" ng-hide='loading==1' ng-click="saveSettings()" ><i class="icon-check"></i> <? print(lang('APPLY','devicetypeedit')) ?></a>
  </li>
</ul>
<!-- HELP -->
<div class="collapse" id="collapseHelp">
  <div class="card card-body">
    <? print(lang('HELP1','devicetypeedit')) ?>
  </div>
  <br/>
</div>
<div class="alert alert-success" ng-cloak ng-show="successmsg"> <button type="button" class="close" ng-click='successmsg=""'>&times;</button>{{successmsg}}</div>
<div class="alert alert-danger" ng-cloak ng-show="errormsg"> <button type="button" class="close" ng-click='errormsg=""'>&times;</button>{{errormsg}}</div>
<div class="alert alert-info" ng-cloak ng-show="infomsg"> <button type="button" class="close" data-dismiss="alert">&times;</button>{{infomsg}}</div>
<div class="alert alert-secondary" ng-cloak ng-show="loading"><i id='iconRepeatMain' class='icon-spinner icon-spin' style='font-size:36px; color:red'></i>&nbsp;<? print(lang('PLEASE_WAIT','devicetypeedit')) ?></div>
<form name="form" class="css-form form-horizontal" novalidate>

<!-- VARIABLES -->
<div ng-init="enabled=<? print(intval($iec104_ini['enabled'])); ?>;"></div>
<div ng-init="send_interval=<? print(intval($iec104_ini['send_interval'])); ?>;"></div>
<div ng-init="port=<? print(intval($iec104_ini['port'])); ?>;"></div>
<div ng-init="address=<? print(intval($iec104_ini['address'])); ?>;"></div>
<div ng-init="num_roles=<? print(intval($iec104_ini['num_roles'])); ?>;"></div>
<div ng-init="callback='<? print(($iec104_ini['callback'])); ?>';"></div>


<div class="card">
    <div class="card-body">
        <h5 class="card-title">IEC 104 Options</h5>
        <p class="card-text">    
            <div class="form-group" ng-cloak>
                <label for="dev_category">IEC104 Support</label>
                <select ng-model="enabled" name='enabled' class="form-control">
	                <option value=0>Disabled</option>
				    <option value=1>Enabled</option>
                </select>
                <small class="form-text text-muted">Enable this to allow IEC104 support</small>
	        </div>
             <div class="form-group">
                <label for="send_interval">Periodic send interval in ms</label>
                <input class="form-control" type='number' ng-model="send_interval"  class='span12' name='send_interval' required integer aria-describedby="rHelp" placeholder="Interval in ms">
                <small id="rHelp" class="form-text text-muted">Interval at which packets get sent</small>
            </div>
         
            <div class="form-group">
                <label for="dev_name">Listening Port</label>
                <input type="number" class="form-control" ng-model="port"  class='span12' ng-maxlength=64 name='dev_name' required aria-describedby="rnHelp" placeholder="Port number">
                <small id="rnHelp" class="form-text text-muted">Default is 2404</small>
            </div>	        
            
            <div class="form-group">
                <label for="dev_name">Address</label>
                <input type="number" class="form-control" ng-model="address"  class='span12' ng-maxlength=64 name='address' required aria-describedby="rnHelp" placeholder="Port number">
                <small id="rnHelp" class="form-text text-muted">Address to respond on</small>
            </div>	        
            <div class="form-group">
                <label for="dev_description">Callback script</label>
                <input type="text" class="form-control" ng-model="callback"  class='span12' ng-maxlength=128 name='callback' required aria-describedby="rnHelp" my-directive placeholder="/path/to/script.cgi">
                <small id="rnHelp" class="form-text text-muted">Execute this callback when a paramete is set from the IEC104 master</small>
            </div>	             
               
        </p>
	</div>
</div>  
<br/>
<br>
<div class="card">
    <div class="card-body">
        <h5 class="card-title text-preimary">Register Mapping</h5>
        <p class="card-text">    
            <div style='float:right'><a name='ot_addact'></a>
                <button class="btn btn-large" ng-click="doEditAll(roles)"><? print(lang('EDIT_ALL','devicetypeedit')) ?></button>
                <button class="btn btn-large" ng-click="doAdd(roles)"><? print(lang('ADD','devicetypeedit')) ?></button>
            </div>
            <table class='table table-striped'>
                <tr>
                    <th scope="col"></th>
                    <th scope="col">Address</th>
                    <th scope="col">Name</th>
                    <th scope="col">Type</th>
                    <th scope="col"><? print(lang('OPTIONS','devicetypeedit')) ?></th>
                </tr>
                <tr ng-repeat="item in roles">
                    <td><a name='role{{$index}}'></a></td>
                    <td><strong ng-show='item.edit!=1'>{{item.address}}</strong><div ng-show='item.edit==1'>
                        <input class='form-control' type='text' ng-model='item.address'>
                        </div></td>            
                    
                    <td><strong ng-show='item.edit!=1'>{{item.name}}</strong><div ng-show='item.edit==1'>
                        <input class='form-control' type='text' ng-model='item.name'>
                        </div></td>            
                    <td><strong ng-show='item.edit!=1' >{{makeType(item.type)}}</strong><div ng-show='item.edit==1'>
                        <select class='form-control' ng-model='item.type'>
                            <option value='0'>Single Point (SP)</option>
                            <option value='1'>Double Point (DP)</option>
                            <option value='2'>Analog (ME)</option>
                            </select>
                        </div></td>
                    <td>
                        <div ng-hide="refresh" class="dropdown">
                            <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                <? print(lang('ACTION','devicetypeedit')) ?> <span class="caret"></span>
                            </button>
                            <div class="dropdown-menu dropdown-menu-right text-dark" >
                                <a ng-show='item.edit!=1' class="dropdown-item" ng-click='item.edit=1' href='javascript:void(0)'><i class="icon-pencil"></i>&nbsp;<? print(lang('EDIT','devicetypeedit')) ?></a>
                                <a ng-show='item.edit==1'  class="dropdown-item" ng-click='stopEdit(role,$index)' href='javascript:void(0)'><i class="icon-pencil"></i>&nbsp;<? print(lang('STOP_EDIT','devicetypeedit')) ?></a>
                                <a class="dropdown-item" ng-show='$index>0' ng-click='moveUp(roles,$index)' href='javascript:void(0)'><i class="icon-up"></i>&nbsp;<? print(lang('MOVE_UP','devicetypeedit')) ?></a>
                                <a class="dropdown-item" ng-show='$index<roles.length-1' ng-click='moveDown(roles,$index)' href='javascript:void(0)'><i class="icon-down"></i>&nbsp;<? print(lang('MOVE_DOWN','devicetypeedit')) ?></a>
                                <a  class="dropdown-item" ng-click='insertItem2(roles,$index+1)' href='javascript:void(0)'><i class="icon-plus"></i>&nbsp;<? print(lang('INSERT_AFTER','devicetypeedit')) ?></a>
                                <a class="dropdown-item" ng-click='deleteItem(roles,$index)' href='javascript:void(0)'><i class="icon-remove"></i>&nbsp;<? print(lang('DELETE','devicetypeedit')) ?></a>
                            </div>
                            </div>
                    </td>    
                </tr>
            </table>
            <div style='float:right'>
                <button class="btn btn-large" ng-click="doEditAll(roles)"><? print(lang('EDIT_ALL','devicetypeedit')) ?></button>
                <button class="btn btn-large" ng-click="doAdd(roles)"><? print(lang('ADD','devicetypeedit')) ?></button>
            </div>
            
        </p>    
 	</div>
</div> 
</form>

<script>
var gItems,gHttp;
var count=0;
var app = angular.module('myApp', []);
var globalScope;
Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};
app.factory('dataService', function($http, $q,$timeout) {
  
  this.async = function(devid) {
    return $http.get('/app/role_ajax.cgi?action=get&index='+devid)
    .then(function (response) {
        //alert(response.data);
      var data = response.data;
      return data;
    }, function(reason) {
        alert('Failed: ' + reason);
        $timeout(poll, 100);  // try again
    });
    
  };
  
  return this;
});


function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}
function IEC104Ctrl(dataService,$scope, $timeout,$http) {
  var self = this;
  globalScope=$scope;
  $scope.infomsg="";
    $scope.settings_locked=settings_locked;
    if (settings_locked) 
        $scope.errormsg="Unable to save settings since the Flash is locked.  Please unlock first.";
  
  
  $scope.roles=[<?
    $num=$iec104_ini['num_roles'];
    for ($i=0;$i<$num;$i++) {
        print("{index:".$i.',name:"'.$iec104_ini['role'.$i.'_name'].'",type:'.$iec104_ini['role'.$i.'_type'].",address:".$iec104_ini['role'.$i.'_address']."},");
   }  
  ?>];

  


  /*$scope.do_orig=$scope.do.splice(0,$scope.do.length);
  $scope.di_orig=$scope.di.splice(0,$scope.di.length);
  $scope.rw_orig=$scope.rw.splice(0,$scope.rw.length);
  $scope.ro_orig=$scope.ro.splice(0,$scope.ro.length);
  $scope.roles_orig=$scope.roles.splice(0,$scope.roles.length);
  */
  $scope.doAdd=function(arr) {
    if (arr==$scope.roles) {
        $scope.num_roles++;$scope.roles.push({edit:1,name:'newrole'+$scope.num_roles})
    }
  }
  $scope.doEditAll=function(arr) {
    if (arr==$scope.roles) {
         for (i=0;i<$scope.num_roles;i++) {
            $scope.roles[i].edit=1;
         }

    }

  }
   function swapElement(array, indexA, indexB) {
     var tmp = array[indexA];
    array[indexA] = array[indexB];
    array[indexB] = tmp;
  }
  
  $scope.moveUp=function(arr,idx) {
     if (idx>0)
        swapElement(arr,idx-1,idx);
  }
  
  $scope.insertItem2=function(arr, idx) {
          //alert(idx);

    if (arr==$scope.roles) {
        $scope.num_roles++;$scope.roles.insert(idx,{edit:1,name:'newrole'+$scope.num_roles});
    }
  }
  $scope.moveDown=function(arr,idx) {
     if (idx<arr.length-1)
        swapElement(arr,idx,idx+1);
  } 
  $scope.stopEdit = function ( arr,idx ) {
    arr[idx].edit=0;
    return 1;
  }
  
  

  $scope.deleteItem = function ( arr,idx ) {
      var person_to_delete = arr[idx];

    //API.DeleteAction({ id: person_to_delete.id }, function (success) {
        arr.splice(idx, 1);
         if (arr==$scope.di) {
        $scope.num_di--;
    }
    if (arr==$scope.roles) {
        $scope.num_roles--;
    }
    //});
  };
  

  
  $scope.makeType=function(id) {
    switch (Number(id)) {
        case 0:return "Single Point (SP)";
        case 1:return "Double Point(DP)";
        case 2:return "Analog value (ME)";

    }
  }
  
  $scope.makeRoleClass=function(id) {
    switch (Number(id)) {
        case 0:return "badge-dark";
        case 1:return "badge-dark";
        case 2:return "badge-success";
        case 3:return "badge-danger";
        case 22:return "badge-success";
        case 23:return "badge-danger";
        case 24:return "badge-success";
        case 25:return "badge-success";
        case 26:return "badge-success";
        case 27:return "badge-success";
        case 28:return "badge-success";
        case 29:return "badge-danger";
        case 30:return "badge-danger";
        case 31:return "badge-success";
        case 34:return "badge-danger";
        case 35:return "badge-danger";
        case 36:return "badge-success";
        case 38:return "badge-success";
        case 39:return "badge-success";
        case 40:return "badge-success";
        case 41:return "badge-success";
        
    }
  }
  $scope.hexToDec=function(hex) {
    var result = 0, digitValue;
    hex = hex.toLowerCase();
    if (hex.substr(0,2)=='0x') hex=hex.substr(2);
    for (var i = 0; i < hex.length; i++) {
        digitValue = '0123456789abcdefgh'.indexOf(hex[i]);
        result = result * 16 + digitValue;
    }
    return result;
 }
 
  $scope.$watch('hexval', function(asyncData) {
    //alert("changed");
    if (asyncData) 
        $scope.decval=$scope.hexToDec($scope.hexval);
  });

  $scope.makeText=function(item) {
        return item.name;
        return item.device_id;
    }
  $scope.saveAsNew=function () {
    $scope.role_id=-1;
    $scope.saveSettings();
  }
  $scope.saveSettings=function () {
      $scope.successmsg="";
      $scope.errormsg="";
      $scope.infomsg="";
      $scope.role_devices="";
      $scope.loading=1;
      data ="[config]\n"+
            "num_roles="+$scope.num_roles+"\n"+
            "address="+$scope.address+"\n"+
            "server=1\n"+
            "send_interval="+$scope.send_interval+"\n"+
            "callback="+$scope.callback+"\n"+
            "port="+$scope.port+"\n"+
            "enabled="+$scope.enabled+"\n";
        for (i=0;i<$scope.num_roles;i++) {
                    data+="role"+i+"_name="+($scope.roles[i].name)+"\n";
                    data+="role"+i+"_type="+$scope.roles[i].type+"\n";
                    data+="role"+i+"_address="+$scope.roles[i].address+"\n";
        }

    //document.getElementById("statusmsg").innerHTML="<i>Saving file, please wait...</i>";
    fn = "/config/iec104.ini";
    
	/*$.ajax({
          type: "PUT",
          url: fn,
          data: data,
          dataType: "html"
        }).done(function( msg ) {
           if (msg.substr(0,2)!="OK") alert(msg);
          //alert( "Data Saved: " + msg );
          globalScope.apply(function() {
        //  alert("OK!");
        	$scope.successmsg="Settings saved. You will need to reboot for changes to take effect";
          });
        	//document.getElementById("successmsg").innerHTML="Settings saved. You will need to reboot for changes to take effect";
        });    
      */  
      //alert(data);return;    
      role_url='/app/configsavedev.cgi?config=dev'+$scope.dev_type_id+'.ini&section=config';
      $http.put(fn,data)
        .then(function (response) {
        //alert(response.data);
          //if (response=="OK")
          var data = response.data;
          $scope.loading=0;
          //alert(data);
          if (data=="OK")
            $scope.successmsg="Settings saved. You will need to reboot for changes to take effect";
          else {
            $scope.errormsg=data;
          }
          return data;
        }, function(reason) {
            $scope.errormsg="Error saving settings";
            $scope.loading=0;
            $scope.successmsg="";
            //alert('Failed: ' + reason);
           
        });
   }

}


</script>
<? include("/app/footer.inc"); ?>      











