MobileChromeApps / cordova-plugin-chrome-apps-sockets-tcp

BSD 3-Clause "New" or "Revised" License
44 stars 52 forks source link

Are Cordova Chrome App(CCA) plugins compatible w/ Cordova/ionic app #4

Closed bmwertman closed 8 years ago

bmwertman commented 8 years ago

I have an ionic app that needs to make a TCP socket connection between it and a Wifi access point(no internet just a local access point).

I was able to create the TCP socket, connect, send and receive with Chrome Sockets(Deprecated) . But I always got the same string back in the response that I sent over the connection which was not the expected behavior.

When I moved to this supported Chrome Sockets TCP lib I could create the socket but not connect to it. I came across this CCA command line tool tutorial after seeing some Stack Overflow posts talking about a manifest.mobile.json file which I did not have in my ionic app.

I installed the CCA command line tool and ran through the tutorial to create a basic CCA app. I grabbed the generated manifest.mobile.json file from that and threw it in the www folder of mine and tried to connect again with no better results. Here is the generated manifest.mobile.json file with a few lines I added;

 {
  "packageId": "com.your.company.ChromeApp",
  // Android-only overrides here (e.g. icons, name, version)
  "android": {
  },
  // iOS-only overrides here (e.g. icons, name, version)
  "ios": {
  },
  // Uncomment to use system WebView rather than Crosswalk
   "webview": "system",

  // Uncomment to enable use of eval()
   "cspUnsafeEval": true,

  // Uncomment to enable use of inline <script>
   "cspUnsafeInline": true,

   "sockets": {
      "tcp": {"connect" : "*"}
   }
}

And this is the code used to make and connect to the tcp socket

 chrome.sockets.tcp.create(function(createInfo) {
            chrome.sockets.tcp.connect(createInfo.socketId, 'MY_IP', MY_PORT, function(result) {
              if (result === 0) {
                chrome.sockets.tcp.send(createInfo.socketId, PID, function(result) {
                  if (result.resultCode === 0) {
                    console.log('connectAndSend: success');
                    chrome.sockets.tcp.disconnect(createInfo.socketId);
                    chrome.sockets.tcp.close(createInfo.socketId);
                  }
                });
              }
            });
          });

And the error message I get

failed to connect to /MY_IP (port MY_PORT): connect failed: ENETUNREACH (Network is unreachable)
callbackWithError @ errors.js:30
fail @ sockets.tcp.js:68
cordova.callbackFromNative @ cordova.js:293
processMessage @ cordova.js:1079
processMessages @ cordova.js:1102
agrieve commented 8 years ago

The socket plugins should not require a cca-based app, and should work fine in a normal Cordova application.

There's also some good example code showing how to use them here: https://github.com/apache/cordova-app-harness/blob/master/www/cdvah/js/HttpServer.js

and in its tests here: https://github.com/MobileChromeApps/cordova-plugin-chrome-apps-sockets-tcp/blob/master/tests/tests.js

bmwertman commented 8 years ago

Thanks. I grabbed the methods you had in the tests.js file from the second link and put them into an angular factory. As an example here is the connectAndSend method along with stringToArrayBuffer;

app.factory('$chrometcp',[function(){
  connectAndSend: function(data, addr, port) {
      chrome.sockets.tcp.create(function(createInfo) {
        chrome.sockets.tcp.connect(createInfo.socketId, addr, port, function(result) {
          if (result === 0) {
            chrome.sockets.tcp.send(createInfo.socketId, data, function(result) {
              if (result.resultCode === 0) {
                console.log('connectAndSend: success');
                chrome.sockets.tcp.disconnect(createInfo.socketId);
                chrome.sockets.tcp.close(createInfo.socketId);
              }
            });
          }
        });
      });
    },
    stringToArrayBuffer: function(string) {
      var buf = new ArrayBuffer(string.length);
      var bufView = new Uint8Array(buf);
      for (var i = 0, strLen = string.length; i < strLen; i++) {
        bufView[i] = string.charCodeAt(i);
      }
      return buf;
    }
}]);

And my controller where I inject it to use. I'm also using a cordova wifi plugin to manage the wifi connection;

app.controller('CheckinCtrl', [ '$scope', '$chrometcp',function ($scope, $chrometcp) {
  $scope.OBDPlugNames = [];
  function connectToPlug(){
    var config = $cordovaWifiWizard.formatWPAConfig($localstorage.get('wifiName'), $localstorage.get('wifiPassword'));
    $cordovaWifiWizard.isWifiEnabled().then(function (bool){
      if(bool){//It's enabled
        $cordovaWifiWizard.getCurrentSSID().then(function (res){
          if(res !== config.SSID){//connected to a wifi network besides the OBDII plug?
            $cordovaWifiWizard.disconnect(res).then(function (){//disconnect from current network
              $cordovaWifiWizard.connectNetwork(config.SSID)//connect to OBDII plug
              .then(function (res){
                $scope.OBDConnected = true;
                var PID = $chrometcp.stringToArrayBuffer("010F0D");
                var disableEcho = $chrometcp.stringToArrayBuffer("ATE0");
                $chrometcp.connectAndSend(disableEcho,'XXX.XXX.X.XX', 35000)
              });
            });
          } else {
            $cordovaWifiWizard.connectNetwork(config.SSID)//connect to OBDII plug
            .then(function (res){
              $scope.OBDConnected = true;
              var PID = $chrometcp.stringToArrayBuffer("010F0D");
              var disableEcho = $chrometcp.stringToArrayBuffer("ATE0");
              $chrometcp.connectAndSend(disableEcho,'XXX.XXX.X.XX', 35000)
            });
          }
        });
      } else {//it's disabled
        $ionicPopup.alert({
          template:  "<h3>Wifi is disabled</h3><br><strong><p>Please enable wifi and try again</p></strong>"
        });
      }
    });
  }

  $scope.wifi = {};

  $scope.readVIN = function(){

    if($localstorage.get('wifiPassword') === undefined){
      $ionicPopup.show({
          templateUrl: "checkin/templates/wifi_popup.html",
          scope: $scope,
          buttons: [{text: 'Cancel'}, 
          {text: '<b>Save</b>', 
          type: 'button-positive', 
          onTap: function(e){
            if(!$scope.wifi.network || !$scope.wifi.password){
              e.preventDefault();
            } else {
              $localstorage.set('wifiPassword', $scope.wifi.password);
              $localstorage.set('wifiName', $scope.wifi.network);
              if(!$scope.OBDConnected){
                connectToPlug();
              } 
            }
          } 
        }]
      }); 
    } else {
      if(!$scope.OBDConnected){
        connectToPlug();
      }
    }
  }
}]);

Running this gives me the same error as above connect failed: ENETUNREACH. Any other thoughts on why it may not be connecting?

agrieve commented 8 years ago

No ideas :(. Are you sure the target is actually reachable from the device? Maybe try hitting an http server on the same device via an XMLHttpRequest just to make sure?

On Mon, Oct 19, 2015 at 5:40 PM, Brad Wertman notifications@github.com wrote:

Thanks. I grabbed the methods you had in the tests.js file from the second link and put them into an angular factory. As an example here is the connectAndSend method along with stringToArrayBuffer;

app.factory('$chrometcp',[function(){ connectAndSend: function(data, addr, port) { chrome.sockets.tcp.create(function(createInfo) { chrome.sockets.tcp.connect(createInfo.socketId, addr, port, function(result) { if (result === 0) { chrome.sockets.tcp.send(createInfo.socketId, data, function(result) { if (result.resultCode === 0) { console.log('connectAndSend: success'); chrome.sockets.tcp.disconnect(createInfo.socketId); chrome.sockets.tcp.close(createInfo.socketId); } }); } }); }); }, stringToArrayBuffer: function(string) { var buf = new ArrayBuffer(string.length); var bufView = new Uint8Array(buf); for (var i = 0, strLen = string.length; i < strLen; i++) { bufView[i] = string.charCodeAt(i); } return buf; } }]);

And my controller where I inject it to use. I'm also using a cordova wifi plugin to manage the wifi connection;

app.controller('CheckinCtrl', [ '$scope', '$chrometcp',function ($scope, $chrometcp) { $scope.OBDPlugNames = []; function connectToPlug(){ var config = $cordovaWifiWizard.formatWPAConfig($localstorage.get('wifiName'), $localstorage.get('wifiPassword')); $cordovaWifiWizard.isWifiEnabled().then(function (bool){ if(bool){//It's enabled $cordovaWifiWizard.getCurrentSSID().then(function (res){ if(res !== config.SSID){//connected to a wifi network besides the OBDII plug? $cordovaWifiWizard.disconnect(res).then(function (){//disconnect from current network $cordovaWifiWizard.connectNetwork(config.SSID)//connect to OBDII plug .then(function (res){ $scope.OBDConnected = true; var PID = $chrometcp.stringToArrayBuffer("010F0D"); var disableEcho = $chrometcp.stringToArrayBuffer("ATE0"); $chrometcp.connectAndSend(disableEcho,'XXX.XXX.X.XX', 35000) }); }); } else { $cordovaWifiWizard.connectNetwork(config.SSID)//connect to OBDII plug .then(function (res){ $scope.OBDConnected = true; var PID = $chrometcp.stringToArrayBuffer("010F0D"); var disableEcho = $chrometcp.stringToArrayBuffer("ATE0"); $chrometcp.connectAndSend(disableEcho,'XXX.XXX.X.XX', 35000) }); } }); } else {//it's disabled $ionicPopup.alert({ template: "

Wifi is disabled


Please enable wifi and try again

" }); } }); }

$scope.wifi = {};

$scope.readVIN = function(){

if($localstorage.get('wifiPassword') === undefined){
  $ionicPopup.show({
      templateUrl: "checkin/templates/wifi_popup.html",
      scope: $scope,
      buttons: [{text: 'Cancel'},
      {text: '<b>Save</b>',
      type: 'button-positive',
      onTap: function(e){
        if(!$scope.wifi.network || !$scope.wifi.password){
          e.preventDefault();
        } else {
          $localstorage.set('wifiPassword', $scope.wifi.password);
          $localstorage.set('wifiName', $scope.wifi.network);
          if(!$scope.OBDConnected){
            connectToPlug();
          }
        }
      }
    }]
  });
} else {
  if(!$scope.OBDConnected){
    connectToPlug();
  }
}

} }]);

Running this gives me the same error as above connect failed: ENETUNREACH. Any other thoughts on why it may not be connecting?

— Reply to this email directly or view it on GitHub https://github.com/MobileChromeApps/cordova-plugin-chrome-apps-sockets-tcp/issues/4#issuecomment-149355391 .

bmwertman commented 8 years ago

What I'm trying to connect to is this OnBoard Diagnostic(OBDII) Wifi adapter. I have no way of setting up an http server on it. I was able to connect to it with the deprecated Chrome Socket library passing the tcp parameter though. As I mentioned above I just kept getting the same command string I sent back as a response.

bmwertman commented 8 years ago

For additional info I have an open issue over at Scantool.net Forum on trying to make this work.

bmwertman commented 8 years ago

OK, I'm getting intermittent successful connections. It seems I have to set a delay between when I create the socket and try to connect to it. The amount of delay that works varies. 500ms might work once then I have to bump it to 600ms. But generally between 500-800ms. And It never connects on the first attempt. I've set my code up so that it repeatedly tries to connect. It usually gets it on the second or third try.

Now I'm having trouble with getting the chrome.sockets.tcp.setKeepAlive() to set.

screen shot 2015-10-20 at 4 30 12 pm

$cordovaWifiWizard.connectNetwork(config.SSID)//connect to OBDII plug
            .then(function (res){
              $scope.OBDConnected = true;
              chrome.sockets.tcp.create(function (created){
                console.log(created);
                connect();
                function connect(){
                  connectAttempts++
                  chrome.sockets.tcp.connect(created.socketId, '192.168.0.10', 35000, function (result){
                    if(result !== 0 && connectAttempts < 5){
                      console.log("Didn't connect! Trying again.");
                      $timeout(function(){
                        connect();
                      }, 600);
                    } else {
                      console.log(result);
                      chrome.sockets.tcp.setKeepAlive(created.socketId, true, function (result){
                        console.log(result);
                        chrome.sockets.tcp.onReceive.addListener( function (info){
                          console.log(info)
                          if(i < ATCommands.length){ 
                            sendATCommand();
                          } else if(i === ATCommands.length) {
                            sendPID();
                          } else {
                            chrome.sockets.tcp.disconnect(info.socketId);
                            chrome.sockets.tcp.close(info.socketId);
                          }
                        }); 
                        chrome.sockets.tcp.onReceiveError.addListener( function (err){
                          console.log(err)
                          chrome.sockets.tcp.disconnect(err.socketId);
                          chrome.sockets.tcp.close(err.socketId);
                        });
                        function sendATCommand(){
                          chrome.sockets.tcp.send(created.socketId, $chrometcp.stringToArrayBuffer(ATCommands[i]), function (sendInfo){ 
                            i++;
                            console.log(sendInfo); 
                          });
                        } 
                        function sendPID(){
                          chrome.sockets.tcp.send(created.socketId, $chrometcp.stringToArrayBuffer(PID), function (sendInfo){ 
                            i++;
                            console.log(sendInfo); 
                          });
                        } 
                      });
                    }
                  });
                }
              });
            });
          }
        });
bmwertman commented 8 years ago

This is the other error I'm seeing on the times when it fails to connect screen shot 2015-10-20 at 5 12 11 pm

bmwertman commented 8 years ago

I was able to make the connection and complete my request. Two issues were preventing it. First, I had to revert back to using the deprecated chrome API chrome.socket.create( 'tcp', "IP", Port, cb). It seems there was a timeout, as shown in my last post, that was happening before the connection could be made. The reason I was not getting the expected response with the deprecated chrome.socket had nothing to do with the API itself. My OBD device wanted a carriage return at the end of the request being sent that I was formatting incorrectly.

WoodyWoodsta commented 8 years ago

Glad you managed to sort it out!