Websockets to update angular mobile app data realtime

Fairly new to ignition. Looking to find out what my options might be to create a WebSockets server for Ignition our current version 8.0.4. right now I use WebDev with some APIs designed to return incremental tag data but real-time in the app without http polling would be ideal. Is there an opportunity for us to build our own servlet that could send us that data or does that require special licensing? I’ve considered using push notifications with firebase to remove the polling element but it can be slow or unreliable from time to time. Does inductive have a place for future enhancement requests? Could WebDev be expanded to allow creation of WebSocket endpoints?

Feature requests are handled at https://ideas.inductiveautomation.com

If you’ve got Java experience you can probably use module SDK to mount your own WebSocket servlet instead of using the WebDev module.

1 Like

And that doesn’t require any additional licensing? I’m a little foggy on the module addition stuff it seems like there is a license for it for third party but there also is free ability to add yourself. Is the distinction if you want to sell it? Where would be the best place to seek help with the SDK? I’m assuming at some point we’ll get stuck, is that place here in the forum?

No additional license or cost is required to use the SDK.

If you build a module you’re free to sell it, or not. The only interaction you need to have with us is getting a secondary license with your module ID on it, if you care about that (no cost). If you’re not going to sell the module then simply don’t build any licensing or trial logic into it and it won’t matter if it’s listed on a license or not.

There’s some pretty out of date docs here.
There’s an examples repository on GitHub.
Other than that there is unofficial support here in the forum :slight_smile:

Adding a Servlet is probably one of the easiest things to do because it’s 10% dealing with the Ignition SDK and 90% knowing how Java and Servlets work… (there’s an addServlet method: WebResourceManager)

1 Like

Really appreciate the clarification and direction provided this will be a great start. Thanks much! I will likely post my findings and code here in case anyone else is looking for a similar feature. I found posts on the forum related to this but no real resolve on them. Thanks again!

We were able to create a sample servlet but can’t understand how to get the unsigned module added to ignition. From some really old posts it sounds like we need to get it in developer mode but not quite sure how to achieve that. Is that some old method in those posts and a bit of a red herring in that case?

You can start Ignition with the flag -Dignition.allowunsignedmodules=true set in ignition.conf.

Details about signing a module are here: https://docs.inductiveautomation.com/display/SE/Module+Signing

1 Like

You can also write a simple socket listener in a script:

import socket


# run at the server end (listener)
def Server(ip, port):
	# create the socket
	socket = MySocket(ip, port)
	socket.StartListener()


# run at the client end to send a message to the server
def SendMessage(ip, port, message):
	# create the socket
	socket = MySocket(ip, port)
	socket.SendMessage(message)


class MySocket(object):

	# Constructor
	def __init__(self, ip, port):
		# Initalize variables
		self.ip = ip
		self.port = port
		self.socket = None
		self.enabled = True

	# ----------------------
	# Server Code

	def StartListener(self):
		self.enabled = True
		system.util.invokeAsynchronous(self._listen)
	
	def StopListener(self):
		# set to false to exit the loop
		self.enabled = False

		# close the socket
		if self.socket is not None:
			self.socket.close()
	
	def _listen(self):
		print "Starting Listener on port %d" % (self.port)
		
		# setup the socket listener
		sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
		sock.bind((self.ip, self.port))
		self.socket = sock

		# loop until stopped by StopListener() 
		while self.enabled:
			try:
				data, addr = sock.recvfrom(1024)
				print "%s received '%s'" % (addr, data.decode('utf-8'))
			except:
				pass

		print "Listener on port %d stopped" % (self.port)


	# ----------------------
	# Client Code

	def SendMessage(self, message):
		print "%s:%d send '%s'" % (self.ip, self.port, message)
	
		sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
		MESSAGE = message.encode('utf-8')
		sock.sendto(MESSAGE, (self.ip, self.port))
		sock.close()

2 Likes

I have to review on a PC screen but that appears to be the server listening for data. That could come in handy too but would be the wrong direction for my purposes no?

websockets are different from tcp/ip sockets https://en.wikipedia.org/wiki/WebSocket
websockets support duplex communication over HTTP connection. tcp/ip is request response protocol between a client and a server.

Ah yeah I missed that briefly looking at the code. Needs to be WebSockets for my mobile app to consume them. However there are some interesting concept and methods to note in @DavidWisely’s code block so still much appreciated!

Of course it’s nice to have this code , it’s a completely working tcp code in python , could be handy

We have our web socket module created and loaded it’s echoing responses and logging to the ignition web logger from data sent to it by the client. Our provider context needs to be passed in to wire up the ignition bit and we’re done. A bit of architecture to work out yet to make it flexible for us. Thanks all for the bumps!

1 Like

But I am not sure if Ignition python library supports the socket library. ?Did you try to run this code in Ignition gateway or client?

It does support it, but there are often weird latencies/performance problems. I strongly recommend using the native java networking classes in jython instead.

3 Likes

Thanks. Will this be in general true for all scripting libraries supported by Ignition (such as tags read/writes, data base access etc) or its specific to java networking classes?

Java libraries will always have a performance advantage over the python standard library. It seems to be particularly significant for socket operations.

1 Like

Update: Our java module is sending us messages as our tag changes! It’s working great so far, thanks again for the input all we’ll post another update when it’s complete. It is an animated gif give it a second to see some of the updates LOL. You’ll see the message get’s to us as the ignition designer updates, so performance so far seems quite nice.


4 Likes

Implemented in the mobile app using sockets, When I Emergency stop the machine you can see the BPM start dropping and eventually the machine goes into a stopped state and logs the new meter read.


Here is my really basic implementation of the socket as a singleton service in Angular

import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { ManagerService } from './manager.service';

@Injectable({
  providedIn: 'root'
})
export class PlcsockService {

  public socket: WebSocketSubject<any>;
  public url: string;

  constructor(private managerSvc: ManagerService) {
    this.url = 'ws://256.172.10.300:99999/w';
    this.connect(this.url);
  }

  public connect(iUrl) {
    console.log(`Open Ignition Socket Connection to ${this.url}...`);
    this.socket = webSocket(iUrl);
  }

  public updateTag(iTagPath, iValue) {
    this.socket.next({ tagPath: iTagPath, value: iValue });
  }

  public onError() {
    this.socket.subscribe(
      err => {
        console.log('Ignition Socket Error: ' + err);
        this.managerSvc.ErrorSend('Error in Ignition Socket', err, ['PlcsockService', 'onError']);
      }
    );
  }

  public close() {
    console.log(`Close Ignition Socket Connection to ${this.url}...`);
    this.socket.complete();
  }
}

My component subscribes to the socket to consume the messages

export class PlcsComponent implements OnInit, OnDestroy {

  constructor(private appComponent: AppComponent,
              private spinner: NgxSpinnerService,
              private tel: TelemetryService,
              private managerSvc: ManagerService,
              private router: Router,
              private plcSvc: PlcService,
              private plcsock: PlcsockService) { }

  public plcArray: any;

  ngOnInit() {
    // Get realtime plc updates, this lacks comments yet or a data model don't judge me LOL
    this.plcsock.socket.subscribe(
      msg => {
        console.log(msg); // Any message for tags subscribed from the java module
        if (msg.tagPath.pathParts[0] === 'Status') {
          this.plcArray.forEach(item => {
            if (item.id === msg.tagPath.pathParts[1]) {
              console.log(msg.value.value); // the specific item I want to match up with my on load retrieved data through REST
              item[msg.tagPath.pathParts[2]] = msg.value.value;
            }
          });
        }
      }
    );
  }

  ngOnDestroy() {
    this.plcsock.close();
  }
4 Likes