Cascadas - Protocol for Web Based HMI

From Likindoy

Jump to: navigation, search

In this page we are trying to add all definitions of the new "HMI web communication protocol" we will use in Likindoy. This intend to be the protocol that will be used for MBLogic (Michael Griffin) and Likindoy itself, both for HMI-Server communication.

Contents

Authors

  • Michael Griffin (Canada)
  • Matheus Ledesma (Canada)
  • Juan José Denís (Spain)
  • Juan Miguel Taboada (Spain)

Data Types

The following data types are defined:

  1. Boolean - 0 = False, 1 = True
  2. Integer - Signed decimal integer. The data range is implementation dependent.
  3. Hexadecimal word - The data range is implementation dependent.
  4. Floating point - The data range is implementation dependent.
  5. Time - Time in seconds since the 1st of January 1970 UTC (Unix Epoch). Time may be represented as an integer or floating point number. The precision of the time value is platform dependent.
  6. String - The maximum length is 256 characters.

Address tags

Address Tags - Definition

An address tag is a string which represents a unique data table memory location, database entry, or other storage location.

Tag Length

Address tags are to be no more that 100 characters long. Some client platforms may have an implementation specific limit which is less than this.

Valid Characters

Address tags may contain the letters 'a' to 'z', 'A' to 'Z', the digits '0' to '9', and '_'. Tags may not start with a number.

Character Encoding Set

UTF-8

User Defined Tags

Definition

A user defined tag is a tag such as an input, output, or other implementation specific storage location. Most tags will be user defined.

Mapping User Defined Address Tags to Server Addresses

The server is responsible for mapping of address tags to whatever internal representation the server uses for storage locations.

The user may define any tag which is not a reserved tag.

Reserved Tags

Certain address tags are designated as "reserved". These are predefined to represent certain values which the server is expected to provide. These tags may not be used by the client for any other purposes.

Standard Read Only Reserved Tags

The following tags may be sent by the client to the server in a read request. The server must respond with a valid reply. The response is returned as with any normal read request.

  1. timeutc = Time. Time in UTC.
  2. timelocal = Time. Local time at a specified location. The location chosen as local is implementation dependent and may be different for each client.
  3. clientversion = String. The current version of the copy of the client present at the *server*. This is to allow web clients to determine if a different version of the client is present at the server. This is intended to allow automatic upgrades or version roll-backs. How the client makes use of this information is implementation dependent.
  4. protocolversion = String. The current version of the Cascadas protocol supported.

Implementation Defined Reserved Tags

An implementation may define additional reserved tags according to need. These may be read-only, write-only, or read-write.

Protocol Transport Layer

Data may be transported by the following means:

  1. http
  2. TCP/IP Socket

HTTP

A request is made by http POST to a valid end point with the identifier 'cascadas' and with the request carried in the headers. A response is returned as another document.

TCP/IP Socket

Requests and responses may be transported over a TCP/IP socket connection.

Protocol Representation

Definition

Data representation is based on JSON (JavaScript Object Notation). This is a popular lightweight protocol that is readily supported in internet applications. In JSON, data is encapsulated in "object literals". These are also very similar to Python dictionaries, allowing similar semantics when that language is used in server applications.

Data items are stored in key/value pairs, with objects capable of being nested.

More information about JSON may be found at www.json.org


Message Field Definitions

The following message field keys are defined:

id

Type: String.

Valid for: Request, response.

Description: An arbitrary string. This may appear in a request or response is normally used to identify the client or server. This may be an empty string.

Errors: None. Values with invalid data types are discarded by the recipient. Strings exceeding the maximum allowed string length are truncated by the recipient.

Example Request: "id" : "HMI 9876 from Water Pressure INC."

Example Response: "id" : "Scada 10934 from Water Pressure INC."

msgid

Type: Positive unsigned 16 bit integer (0 - 65535).

Valid for: Request, response.

Description: This is used to identify a message. The msgid is sent in a request by the client, and echoed back by the server in its response. This should normally increment with each request, returning to zero when it reaches its maximum value.

Errors: None. Values with invalid data types or out of range values are discarded by the recipient. If the server receives an invalid msgid from a client it should respond with a msgid of 0.

Example Request: "msgid" : 12345

Example Response: "msgid" : 12345

stat

Type: String.

Valid for: Request, response.

Description: This is a command {Note: Not defined at this time}

Errors: Undefined.

Example Request: "stat" : "start"

Example Response: "stat" : "ok"

timestamp

Type: Time.

Valid for: Response

Description: A value of type "Time", representing the current time as UTC (GMT).

Errors: None. Values with invalid data types or out of range values are discarded by the recipient.

Example Response: "timestamp": 129873294

read

Type: Array (request), Object (response).

Valid for: Request, response.

Description: In a request, this is an array (list) of tags for which the client wishes to receive the current data values. In a response, this is an object (dictionary) of key/value pairs containing the tags and their corresponding data values.

Errors: If an error occurs, the affected tag will not be returned in the response, but will be returned with an error code in the "errors" field.

Example Request: "read" : ["PB1", "PB2", "LS1", "SS5", "TankLevel"]

Example Response: "read" : {"PB1" : 1, "PB2" : 0, "LS1" : 0, "SS5" : 1, "TankLevel" : 50.67}

write

Type: Object.

Valid for: Request.

Description: This exists only in a request and is never returned in a response. This is a object (dictionary) containing key/value pairs which the client wishes to write to the server.

Errors: If an error occurs, the affected tag will be returned with an error code in the "errors" field. If an error has occurred, the write operation may not have completed.

Example Request: "write" : {"SOL1" : 0, "SOL2" : 1, "Pump1Speed" : 2250}

errors

Type: Object.

Valid for: Response.

Description: This exists only in a response and is never sent in a request. This is a object (dictionary) containing key/value pairs. Each value is a further nested object (dictionary) containing more key/value pairs.

The keys to the first (outer) level represent fields (such as "read", "write", etc.) for which an error may have occurred. The values are the nested objects. Valid keys are: read, write. {Note: alarm and event errors are not defined yet.}

The keys to the second (inner) level represent address tags, and the values represent error codes. All error codes are strings.

An error response may contain any number of errors, but only one error will be returned for each tag. If the errors field is empty, there were no errors.


Error Codes:

  1. tagnotfound - The address tag is not recognised by the server.
  2. badtype - The data value is of an incompatible type.
  3. outofrange - The data value is out of range. Range limits may be imposed by the server based on data type or through configuration limits imposed on a per-tag basis.
  4. writeprotected - An attempt was made to write to an address which is write protected or otherwise not writeable.
  5. addresserror - An error occurred in attempting to map the tag to the internal server address representation.
  6. servererror - An unspecified error has occurred in the server which prevents the request from being completed.
Example Response: "errors" : {}

Example Response: "errors" : {"read" : {"PB1" : "tagnotfound", "TankLevel" : "servererror"}}

Example Response: "errors" : {"read" : {"PB1" : "tagnotfound"}, "write" : {'TankLevel' : "outofrange"}}

readable

Note: This key is under discussion on the mailing list and is only tentatively approved at this time.

Type: Object.

Valid for: Request, response.

Description: This is an object (dictionary) containing key/value pairs. The keys represent tags, and the values represent requested types (request) or status (response). The client provides a list of keys and associated types it would like the server to examine. If all keys are types are acceptable, the server returns an empty object. If there are any errors, the server returns the key which represents the error along with an error code.

Errors: All errors are returned within the response, not the "errors" field.

Example Request: "readable" : {"PB1" : "boolean", "LS1" : "boolean", "SS5" : "boolean", "TankLevel" : "integer"}

Example Response:  "readable" : {}

Example Response:  "readable" : {"PB1" : "notfound",  "TankLevel" : "typeerror"}

writeable

Note: This key is under discussion on the mailing list and is only tentatively approved at this time.

Type: Object.

Valid for: Request, response.

Description: This is an object (dictionary) containing key/value pairs. The keys represent tags, and the values represent requested types (request) or status (response). The client provides a list of keys and associated types it would like the server to examine. If all keys are types are acceptable, the server returns an empty object. If there are any errors, the server returns the key which represents the error along with an error code.

Errors: All errors are returned within the response, not the "errors" field.

Example Request:  "writeable" : {"SOL1" : "boolean", "SOL2" : "boolean", "Pump1Speed" : "integer"}

Example Response:  "writeable" : {}

Example Response:  "writeable" : {"SOL1" : "notfound", "SOL2" : "readonly", "Pump1Speed" : "typeerror"}


events

Note: This field is under discussion on the mailing list and is only one of several proposals at this time.

Proposal 1

Type: Object.

Valid for: Response.

Description: This is an object (dictionary) containing key/value pairs. The keys represent event tags, and the values represent event objects. Events do not have to be requested. When an event state changes, it arrives with the next scheduled response message whether requested or not.

Errors: {Note: Undefined}.

Example Response:  "events" : {"event1" : {"time" : 1234567890.12, "value" : 1}}
Proposal 3

Type: Object.

Valid for: Request, response.

Description: This is an object (dictionary) containing key/value pairs. Events are messages concerning unique occurrences which the operator is notified about, but which do not require acknowledgement. Each event has a serial number which uniquely identifies it. The meanings of each are as follows:

  1. "serial" (request and response) - This is a unique integer serial number applied by the server. Each newer event must have a higher serial number than each preceding event. When a client sends a serial number in a request, it is requesting only events with higher serial numbers than that specified.
  2. "max" (request only) - This is an integer sent by a client in a request to indicate the maximum number of responses desired. The server is to respond with no more than this number of events. If the number of available events is greater than this limit, the server will send only the newest events.
  3. "timestamp" (response only) - This is a time value applied by the server indicating when the event occurred.
  4. "event" (response only) - This is a string tag which is used to identify an event message text. The text of event messages is not sent as part of the protocol, but must be supplied by the client.
  5. "state" (response only) - This is a user defined value which may be used to carry additional information relating to the event.


Errors: {Note: Undefined}.

Example Request:  "events" : {"serial" : 1223601864, "max" : 50}
Example Response:  "events" : [{"timestamp" : 1223094615.0, 
	"serial" : 1223094619,  "event" : "PumpStopped", "state" : 1}]

alarms

Note: This field is under discussion on the mailing list and is only one of several proposals at this time.

Proposal 1

Type: Object.

Valid for: Request, Response.

Description: This is an object (dictionary) containing key/value pairs. The keys represent alarm tags, and the values represent either alarm objects (responses) or boolean values (requests). Alarms do not have to be requested. When an alarm state changes, it arrives with the next scheduled response message whether requested or not. A request is sent from a client to the server to acknowledge an alarm. A value of 1 is used to indicate an acknowledgement.

Errors: {Note: Undefined}.

Example Response:  "alarms" : {"alarm1" : {"time" : 1234567890.12, "value" : 1}}
Example Request:  "alarms" : {"alarm1" : 1}
Proposal 3

Type: Object.

Valid for: Request, Response.

Description: This is an object (dictionary) containing key/value pairs. Alarms are messages concerning unique occurrences which the operator is notified about, and which require acknowledgement. Each alarm has a serial number which uniquely identifies it. The meanings of each are as follows:

  1. "serial" (request and response) - This is a unique integer serial number applied by the server. Each newer alarm must have a higher serial number than each preceding alarm. When a client sends a serial number in a request, it is requesting only alarms with higher serial numbers than that specified.
  2. "max" (request only) - This is an integer sent by a client in a request to indicate the maximum number of responses desired. The server is to respond with no more than this number of alarms. If the number of available alarms is greater than this limit, the server will send only the newest alarms.
  3. "timestamp" (response only) - This is a time value applied by the server indicating when the alarm occurred.
  4. "alarm" (response only) - This is a string tag which is used to identify an alarm message text. The text of alarm messages is not sent as part of the protocol, but must be supplied by the client.
  5. "state" (response only) - This is a user defined value which may be used to carry additional information relating to the alarm.
  6. "ackserial" (response only) - This is not used by the alarm, but must be present for compatibility with acknowledgements.
  7. "acktimestamp" (response only) - This is not used by the alarm, but must be present for compatibility with acknowledgements.
  8. "ackclient" (response only) - This is not used by the alarm, but must be present for compatibility with acknowledgements.

Errors: {Note: Undefined}.

Example Request: "alarms" : {"serial" : 12345, "max" : 50}

Example Response:  "alarms" : {"timestamp": 1223094615.0, "serial": 1223094619, 
		"alarm": "PumpStopped", "state": 1, "ackserial" : 0, 
		"acktimestamp" : 0, "ackclient" : ""}


alarmack

Note: This field is under discussion on the mailing list and is only one of several proposals at this time.

Proposal 3

Type: Object.

Valid for: Request, Response.

Description: This is an object (dictionary) containing key/value pairs. Alarms are messages concerning unique occurrences which the operator is notified about, and which require acknowledgement. An alarm acknowledgement ("alarm ack") is an action by the operator which indicates they have seen the alarm. Each alarm acknowledgement has a serial number which uniquely identifies it. The meanings of each are as follows:

  1. "serial" (request and response) - This is a unique integer serial number applied by the server. Each newer alarm ack must have a higher serial number than each preceding alarm ack. When a client sends a serial number in a request, it is requesting only alarm acks with higher serial numbers than that specified.
  2. "max" (request only) - This is an integer sent by a client in a request to indicate the maximum number of responses desired. The server is to respond with no more than this number of alarm acks. If the number of available alarm acks is greater than this limit, the server will send only the newest ones.
  3. "ack" (request only) - This is an object containing one or more request objects.
  4. "timestamp" (response only) - This is a time value applied by the server indicating when the alarm ack was received by the server.
  5. "alarm" (response only) - This is a string tag which is used to identify an alarm message text. The text of alarm messages is not sent as part of the protocol, but must be supplied by the client.
  6. "state" (response only) - This is a user defined value which may be used to carry additional information relating to the alarm.
  7. "ackserial" (response only) - This contains the serial number of the original alarm.
  8. "acktimestamp" (response only) - This contains the time stamp of the original alarm.
  9. "ackclient" (response only) - This contains an implementation dependent key or value which identifies the person or client which acknowledged the original alarm.

Errors: {Note: Undefined}.

Example Request: "alarmack" : {"ackserial" : 12345, "max" : 50, 
		"ack" : [{"timestamp": 1223094615.0, "serial": 1223094619, 
		"alarm": "PumpStopped", "state": 1, "ackserial" : 0, 
		"acktimestamp" : 0, "ackclient" : ""}]}

Example Response:  "alarmack" : [{"timestamp": 1223094615.0, "serial": 1223094619, 
		"alarm": "PumpStopped", "state": 1, "ackserial" : 12346, 
		"acktimestamp" : 1223094679.0, "ackclient" : "Client #1"}]

Message Construction

Order of Fields in a Message

Fields may appear in any order in a message. The protocol does not guarantee the order in which fields will appear will be consistent from message to message.

Mandatory and Optional Fields

Certain fields are mandatory and must appear in all requests and responses.

  1. id
  2. msgid

Other fields are optional and are only required to be included if they contain data.

Example Requests

The following is an example of a typical request:

{
	"id" : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12345,
	"stat" : "start",
	"read" : ["PB1", "PB2", "LS1", "SS5", "TankLevel"],
	"write" : {"SOL1" : 0, "SOL2" : 1, "Pump1Speed" : 2250}
}

Example Responses

The following is an example of a typical response with no errors:

{
	"id"    : "Scada 10934 from Water Pressure INC.",
	"msgid"  : 12347 ,
	"timestamp": 129873294 ,
	"status" : "ok" ,
	"inputs" : { "PB1" : 1 , "PB2" : 0 , "LS1" : 0 , "SS5" : 1 , "TankLevel" : 50.67 } ,
	"errors" : {} 
}

Alarms and Events

Note: This entire section is under active discussion on the mailing list, and will change drastically soon. The proposals all involve simplifying the messages.

Alarms are conditions which must be acknowledged by the operator. Events are conditions which the operator is notified of, but which is not require to be acknowledged.

Alarms and events are optional keys.

Proposal 1

Examples of alarms and events in the server response.


"alarms" : 	{12345: {"device" : "tank1","time": 1220411890.27, "state" : "active", 
			"msgid" : "T1Ovflw", "msg" : "Tank 1 overflow."}, 
		12346 : {"device" : "cyl1","time": 1220411891.27, "state" : "active", 
			"msgid" : "Cyl1R", "msg" : "Press cylinder not returned."}
		}


"events" : 	{12347 : {"device" : "SC42","time": 1220411893.27, "state" : "once",  
			"msgid" : "CC42", "msg" : "Sound cannon 42 triggered."}, 
		12348 : {"device" : "guard","time": 1220411894.27, "state" : "departed",  
			"msgid" : "Guard1", "msg" : "Guard door opened."}
		}

Each alarm or event is defined with a unique key generated by the server. This key is what is used to identify the unique occurrence of each alarm or event. Since each instance of an alarm or event is unique, this key should not be reused.

The data is an object containing the following keys:

  1. time = Time. Time as UTC. This is the time stamp applied by the server.
  2. device = String. This is an arbitrary string that allows the client to associate the alarm with a particular device.
  3. msgseq = Integer. This is a unique value used to identify the specific occurance of the alarm or event.
  4. state = String.
    1. active = The condition causing the alarm or event is currently active. This is for conditions with a sustained state.
    2. departed = The condition causing the alarm or event condition has deactivated. This is for conditions with a sustained state.
    3. once = The alarm or event condition has occurred, but has no current reported state. This is for conditions which naturally have a transitory state.
  5. msgid = String. This is a code which is used to identify the alarm or event message. This allows a message to be identified even if the actual text is changed or if different languages are logged. If an alarm or event is no longer used, this code should not be recycled.
  6. msg = String. This is any arbitrary text. This is normally a description of the alarm or event. If the server supports multiple languages this should be in the user selected language and locale.

Dismissing and Acknowledging Alarms

The server should send a new alarm or event in the next scheduled server response. The client can "dismiss" the message by writing to the tag location with the 'msgseq'. Dismissing a message does not acknowledge it. It simply informs the server that it no longer as to send this message. The operator can acknowledge the alarm by writing to the tag location with the 'msgseq' and an 'ack'.

E.g. This following will dismiss the alarm.


"alarms" : {12345 : "dismiss"}

This will acknowledge the alarm.


"alarms" : {12345 : "ack"}

Dismissing an alarm or event does not acknowledge it. It merely informs the server that the message arrived at the client, and it no longer has to be repeated.

Proposal 3

Messages

Events and alarms are considered to be messages. They occur from time to time, but unlike tags, they are transitory and do not have a permanent existence. They come into existence, are seen (and possibly acknowledged) by the operator, and then pass on to be either logged for historical purposes, or simply discarded.

The other difference between a message and a tag is that while the value of a tag may change from time to time, the tag itself always represents the same item. That is, a tag represents a particular sensor or actuator whose value changes from time to time. The history of these changes in values are not significant so long as we know the correct value at the present time.

A message on the other hand represents a unique occurrence. Each occurrence of an individual event or alarm must be distinguished from every other occurrence.

Message Serial Numbers

Since a message does not have a permanent existence, we apply a serial number to each occurrence of it.

A client will request a message (event, alarm, or alarm acknowledge) by requesting a serial number. The server will send any messages newer than that serial number (up to specified maximum). If the server does not have any messages newer than that serial number, it will not send any messages in its response.

Serial numbers are always generated by the server. Serial numbers are always integers, but the starting point, range, and amount incremented each time is implementation dependent.

Event Sequence

  1. On start-up a client does not know what the latest serial number in the server is. It therefore should make its first request with a very low number, such as 0.
  2. The server will respond with the most recent messages, up to the limit specified in "max". The server may respond with fewer messages than specified by "max" if there are fewer messages available, or if it is desired to limit the size of server responses to some lower quantity.
  3. The client will receive the messages, and display them to the operator in some suitable fashion.
  4. The client will record the largest serial number in the response for use in the next request.
  5. In the client's next request, the recorded serial number will be used to request any messages newer than those sent in the previous response. If there are no new messages, then none will be sent in the next response.
  6. When new messages arrive, the new largest serial number is recorded for use in the next transaction.
  7. The request/response process repeats as described above.

A client will typically display events to an operator in chronological order in a table or list using the information provided by the event message.

Alarm Sequence

The start up and receive sequence for alarms messages is the same as for event messages. However, the client must also be able to response to acknowledge alarms (see below).

Alarm Acknowledge Sequence

The start up and receive sequence for alarm acknowledge messages is similar to that for events, with the following differences.

  1. When the operator wishes to acknowledge an alarm, they press the "acknowledge" button (or whatever the acknowledge action may consist of) on the client.
  2. The client then sends the complete alarm message (in the same form as received from the server) back to the server as part of an "alarmack" request.
  3. The server will read the serial number ("serial") from the message.
  4. The server will use the serial number to locate the original alarm message in its alarm "buffer" (list of active alarms).
  5. The server will move the alarm message from its alarm buffer to its alarm acknowledge buffer (or change its database attribute, depending upon how it was implemented).
  6. The server will transpose the existing "serial" and "timestamp" values to the corresponding "ackserial" and "acktimestamp" locations in the message.
  7. The server will fill in the "serial" and "timestamp" locations with a new serial number corresponding with the next available acknowledge serial number and a new timestamp corresponding to the time at which the message was received by the server.
  8. The server will fill in the "ackclient" location in the message with information which identifies the client or operator.
  9. When the client requests any new alarm acknowledge messages (by the normal means), it is sent the new message.

When the message is received by the client:

  1. When the new alarm acknowledge is received by the client, the client should delete, hide, remove, or mark the old alarm message to indicate it is no longer valid.
  2. The new alarm acknowledge message may be displayed to the operator in a list, table, or some other suitable manner.

If a server receives an acknowledge message for which it has no corresponding alarm message, or if the alarm has already been acknowledged, it will ignore the new alarm acknowledge message.

Dealing with Multiple Clients

Event and alarm messaging is compatible with multiple clients, in that requesting messages from the server does not affect any data on the server. In addition, all clients are treated equally when acknowledging alarms. Any client may send an acknowledge request, and the results of a successful request are available equally to all clients.

Dealing with Large Numbers of Messages

Since the protocol allows a limit to be placed on how many messages may be sent, it is possible that a client may request messages at a slower rate than they are being generated. This in turn means that some messages may be missed.

This is not however normally a problem. Messages which are being generated at a rate too fast for the client to keep up are also likely being generated at a rate too fast for an operator to read. Since the messaging protocol is intended for use in HMI applications, messages are being generated at that rate indicates an application design problem.

Once scenario which a client must be prepared to deal with however is where a very large number of alarms are waiting to be acknowledged. If the number of waiting alarms is greater than the number of alarms a client can accept, and a client is restarted (and so initialised with only the newest alarms), it is possible that some alarms will be left on the server in the unacknowledged state.

Since the client only asks for alarms which are newer that the latest serial number it has seen, these older alarms will not be requested after the operator may acknowledge all the alarms which were loaded on the client. These alarms may then not be seen by the operator unless the client is reinitialised again.

To avoid this situation, it is recommended that when the client's alarm input buffer is empty, the client should return to requesting unacknowledged alarms using the same initial serial number that it uses when starting up.


Examples

Example of events requests and responses.

Request: "events" : {"serial" : 12345, "max" : 50}
Response: "events" : [{"timestamp": 1223094615.0, "serial": 1223094619, 
		"event": "PumpStopped", "state": 1}]

Example of alarm requests and responses.

Request: "alarms" : {"serial" : 12345, "max" : 50}
Response: "alarms" : [{"timestamp": 1223094615.0, "serial": 1223094619, 
		"alarm": "PumpStopped", "state": 1, "ackserial" : 0, 
		"acktimestamp" : 0, "ackclient" : ""}]


Example of alarm acknowledgement requests and responses.

Request: "alarmack" : {"serial" : 12345, "max" : 50}
Response: "alarmack" : [{"timestamp": 1223094615.0, "serial": 1223094619, 
		"alarm": "PumpStopped", "state": 1, "ackserial" : 12346, 
		"acktimestamp" : 1223094679.0, "ackclient" : "Client #1"}]

Initialisation

The protocol is stateless. That means each request/response cycle is independent of any other request/response cycle and neither the client nor the server needs to remember previous states (except for Notify On Change). However, on starting a client should first check to see if it has permission to read and write all necessary tags and that the data types are as expected before entering into normal operation. If the client does not check access to all address tags, it may find later that due to a programmer error or a configuration change, it doesn't have write access to a tag and the operator is unable to activate an essential device.

A client may check any tag without causing any data changes or other side effects by using the "readable" and "writeable" commands. These check the status and type of read and write tags, but do not perform any read or write operations on those tags.

Once the client has confirmed that everything is OK, it can start normal operation.

Startup

{
	"id" : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12345,
	"stat" : "start",
	"readable" : {"PB1" : "boolean", "PB2" : "boolean", "LS1" : "boolean", "SS5" : "boolean", "TankLevel" : "integer"},
	"writeable" : {"SOL1" : "boolean", "SOL2" : "boolean", "Pump1Speed" : "integer"}
}

Answer from the server about the startup

Server can answer to the client saying that access to some read/write variables is not allowed.

{
	"id" : "Scada 10934 from Water Pressure INC.",
	"msgid" : 12345,
	"timestamp" : 129873294,
	"status" : "ok",
	"errors" : {"readable" : {"LS1" : "read denied"} , "writeable" : {"SOL2" : "write denied, only reading"}}
 }

or if everything was fine:

{
	"id"    : "Scada 10934 from Water Pressure INC.",
	"msgid"  : 12347 ,
	"timestamp": 129873294 ,
	"status" : "ok" ,
	"errors" : {} 
}

Once the client has confirmed that everything is OK, it can start normal operation.

Notify On Change (NOC)

Partial data request

To get changes:

{
	"id"    : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12346,
	"stat"  : "partial",
	"write" : {"SOL1" : 1, "SOL2" : 0, "Pump1Speed" : 1250}
}
  • write tag is optional on every request.
  • Server will answer with changed data only from the last requests.

Full data request

To get all:

{
	"id"    : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12346,
	"stat"  : "full",
	"write" : {"SOL1" : 1, "SOL2" : 0, "Pump1Speed" : 1250}
}
  • write tag is optional on every request.
  • Server will answer with the status of all variables.

Exactly data request

To request the status of a specific variable:

{
	"id"    : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12346,
	"stat"  : "partial",
	"read"  : [ "TankLevel"],
	"write" : {"SOL1" : 1, "SOL2" : 0, "Pump1Speed" : 1250}
}

  • write tag is optional on every request.
  • Server will answer with 'TankLevel' status only.

Agreements

  1. The protocol should be simple, use established web technologies where possible: we seem to have settled on JSON for this.
  2. The HMI will be the client, and the SCADA or soft logic system will be the server.
  3. Data items are referenced by 'tag' or 'label' names, not by server data table addresses or physical memory location. Some examples are 'PB-1' or 'Pump1'.
  4. The client will only ask for data items that it is interested in. It does not need a complete copy of all server memory. The client will send to the server a list of tags or address labels that it wants to read.
  5. It can use simple client polling, where each client request results in one reply from the server. This is useful in factory automation and other similar applications where a few clients are connected on a local private network.
  6. It can also use a notify on change system, were the client sends an initial request, the server responds with a complete reply, and the server then sends repeated updates which only contain changes in data. This is useful in applications where a number of clients may be connected over the Internet.
  7. The client can both read and write data using the protocol. The server has the option of rejecting write attempts, but write protection is not defined in the protocol. Writing data requires including a write field in a client request. However, the majority of message traffic will be concerned with sending data from the server to the client.
  8. The system is compatible with encryption and authentication so that it can be operated over the Internet if desired, but these are not defined as part of the protocol.
  9. We have discussed having a name for the protocol, with one possibility being "Malaga".
  10. We have made several contradictory proposals for the notify on change:
    1. The first response sends all data. Subsequent responses send only changes in data until a period of time has passed (e.g. several seconds). At that point, the server sends all data in the client's tag list to the client. The server then resumes sending only changes. This process repeats indefinitely. The value used for this period of time is sent by the client to the server, but the server will limit this value to an acceptable range.
    2. Another proposal was made similar to the above, but with the periodic full update criteria being based on counting the number of messages sent, rather than being based on a time period.
    3. Another proposal was made similar to the above, but there is no periodic full update, Instead, a proportion of the data from the client's tag list is sent with each response, together with any values which have changed.
    4. A proposal seems to have been made by which only changed data is sent, and no period full updates sent. Full updates would be requested by the client when needed.
    5. Another proposal where if no data has changed, then the server delays sending a response. That is, the server sends responses less frequently when there are no data changes to communicate.
    6. Last proposal said that we don't need NOC mode as we can simulate it with a delayed answer from the server only when the client request for partial data (only changes). The server can have a timeout to answer (empty) if nothing changed or when something change, just to answer straight on that moment.
  11. The protocol will support 3 different requests: startup (for setting up the protocol before transfering data), full (to get a full status of al variables) or partial (to get only changed variables).
  12. The protocol needs timestamp: we seem to have settled on UNIX Epoch format based on number of seconds/miliseconds passed from 1-Jan-1970 UTC.
  13. The protocol needs "version of the protocol".
  14. The protocol will not define events, alarms or alerts: that was part of a long discussions.

Security and Authentication

It was made one proposal about authentication based on 3DES encryption:

  • Server and Scada both have to know the encryption password (each client can have its own and server all of them)
  • Password is never transfered by Internet throught this protocol.
  • The authentication package can not be reused never again, since server always request with a different random string. Either if was sniffed with some network sniffer.
  • The rest of the protocol keeps text open so is easy to debug with some network sniffer.
  • We don't slow down server/Scada with encryption by this protocol (that is left as an adminsitrator's decision).
  • The security over the channel depends on the adminitrators (probably VPN or similar would be enought for this purpose).

Client requests startup

{
	"id"    : "HMI 9876 from Water Pressure INC.",
	"msgid" : 12345,
	"stat"  : "start",
	"read"  : [ "PB1", "PB2", "LS1", "SS5", "TankLevel" ],
	"write" : { "SOL1" : 0, "SOL2" : 1, "Pump1Speed" : 1250 }
}

Server requests authentication

Used algorithm: md5(some_random_string)

{
	"id"       : "Scada 10934 from Water Pressure INC.",
	"msgid"    : 12345,
	"timestamp": 129873294,
	"please_encrypt" : "LKJDF90vlijasf924123490jhf"
}

Client authenticates

Used algorithm: md5( tripledes( string , password ) )

{
       'id'    : "Scada 10934 from Water Pressure INC." ,
       'msgid'      : 12346 ,
       'here_it_is' : '93FD)jASFDFMFIK238DF3F9Jd8fHA3'
}

Server answers

Used algorithm for testing: md5( tripledes( md5 ( the_random_string_generated_before ) , password_of_the_client ) ) and check if is the same than the client sent. MD5 ensure the messages can not be dissasembled by a third middle computer sniffing the network.

Authorization denied:

{
	"id"    : "Scada 10934 from Water Pressure INC.",
	"msgid" : 12346,
	"timestamp": 129873334,
	"error" : "Authorization denied"
}

or with the decision of the initial request.:

{
	"id"    : "Scada 10934 from Water Pressure INC.",
	"msgid"  : 12347,
	"timestamp": 129873334,
	"status" : "ok",
	"inputs" : { "PB1" : 1, "PB2" : 0, "LS1" : 0, "SS5" : 1, "TankLevel" : 50.67 },
	"errors" : {}
}

Use cases

On the practical way the protocol for different HMI-Clients is being used as follows:

WebClient <----> MBServer

  • Initial setup
  • Every XX seconds html-java-client sends a request for the whole list.

Likindoy-HMI <----> Likindoy-DBM

  • Initial setup
  • Requests a full update
  • Flash-client sends a request to the server, asking for changes. If no change happens during 15 seconds the server answers with an empty package, if the change happens before, the server answers before the 15 seconds. Then Flash-client will request again for changes without waiting.
  • Sometimes (every XX minutes) Flash-client can request full update for safety.

What happens if a change sent is lost?


Logging: Likindoy-RTU <----> Likindoy-DBM

  • Changes are stored with their timestamp and number of message in a queue.
  • Every XX seconds, DBM requests changes.

We are still thinking about this!

Alarming: Likindoy-RTU <----> Likindoy-DBM

  • Alarms are stored with their timestamp and number of message in a queue.
  • Every XX seconds, DBM requests changes.

We are still thinking about this