Cascadas - Protocol for Web Based HMI
De Likindoy
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.
Contenido |
Authors
- Michael Griffin (Canada)
- Matheus Ledesma (Canada)
- Juan José Denís (Spain)
- Juan Miguel Taboada (Spain)
Data Types
The following data types are defined:
- Boolean - 0 = False, 1 = True
- Integer - Signed decimal integer. The data range is implementation dependent.
- Hexadecimal word - The data range is implementation dependent.
- Floating point - The data range is implementation dependent.
- 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.
- 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 request. The server must respond with a valid reply. {Note: Which field does the server respond in? Inputs?}
- timeutc = Time. Time in UTC.
- timelocal = Time. Local time at a specified location. The location chosen as local is implementation dependent and may be different for each client.
- 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.
- protocolversion = String. The current version of the Malaga 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:
- http
- TCP/IP Socket
HTTP
A request is made by http POST to a valid end point with the identifier 'malaga' {Note: This is not defined yet} and with the request carried in the body of the document. 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.
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.
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}
Example Request: "stat" : "start" Example Response: "stat" : "ok"
timestamp
Type: Time.
Valid for: ??
Description: ??
Example Request: Example Response:
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.
Example Request: "read" : ["PB-1", "PB-2", "LS-1", "SS-5", "TankLevel"]
Example Response: "read" : {"PB-1" : 1, "PB-2" : 0, "LS-1" : 0, "SS-5" : 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.
Example Request: "write" : {"SOL-1" : 0, "SOL-2" : 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. The keys represent tags, and the values contain error codes. {Note: Errors are not defined yet}
Example Response: "errors" : {}
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.
Example Request: "readable" : {"PB-1" : "boolean", "LS-1" : "boolean", "SS-5" : "boolean", "TankLevel" : "integer"}
Example Response: "readable" : {}
Example Response: "readable" : {"PB-1" : "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.
Example Request: "writeable" : {"SOL-1" : "boolean", "SOL-2" : "boolean", "Pump1Speed" : "integer"}
Example Response: "writeable" : {}
Example Response: "writeable" : {"SOL-1" : "notfound", "SOL-2" : "readonly", "Pump1Speed" : "typeerror"}
alarms
Note: This field is under discussion on the mailing list and is only one of several proposals at this time.
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.
Example Response: "alarms" : {"alarm1" : {"time" : 1234567890.12, "value" : 1}}
Example Request: "alarms" : {"alarm1" : 1}
events
Note: This field is under discussion on the mailing list and is only one of several proposals at this time.
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.
Example Response: "events" : {"event1" : {"time" : 1234567890.12, "value" : 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.
- id
- 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" : ["PB-1", "PB-2", "LS-1", "SS-5", "TankLevel"],
"write" : {"SOL-1" : 0, "SOL-2" : 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" : { "PB-1" : 1 , "PB-2" : 0 , "LS-1" : 0 , "SS-5" : 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.
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:
- time = Time. Time as UTC. This is the time stamp applied by the server.
- device = String. This is an arbitrary string that allows the client to associate the alarm with a particular device.
- msgseq = Integer. This is a unique value used to identify the specific occurance of the alarm or event.
- state = String.
- active = The condition causing the alarm or event is currently active. This is for conditions with a sustained state.
- departed = The condition causing the alarm or event condition has deactivated. This is for conditions with a sustained state.
- once = The alarm or event condition has occurred, but has no current reported state. This is for conditions which naturally have a transitory state.
- 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.
- 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.
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" : {"PB-1" : "boolean", "PB-2" : "boolean", "LS-1" : "boolean", "SS-5" : "boolean", "TankLevel" : "integer"},
"writeable" : {"SOL-1" : "boolean", "SOL-2" : "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" : {"LS-1" : "read denied"} , "writeable" : {"SOL-2" : "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" : {"SOL-1" : 1, "SOL-2" : 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" : {"SOL-1" : 1, "SOL-2" : 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" : {"SOL-1" : 1, "SOL-2" : 0, "Pump1Speed" : 1250}
}
- write tag is optional on every request.
- Server will answer with 'TankLevel' status only.
Agreements
- The protocol should be simple, use established web technologies where possible: we seem to have settled on JSON for this.
- The HMI will be the client, and the SCADA or soft logic system will be the server.
- 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'.
- 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.
- 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.
- 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.
- 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.
- 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.
- We have discussed having a name for the protocol, with one possibility being "Malaga".
- We have made several contradictory proposals for the notify on change:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.
- The protocol needs "version of the protocol".
- 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" : [ "PB-1", "PB-2", "LS-1", "SS-5", "TankLevel" ],
"write" : { "SOL-1" : 0, "SOL-2" : 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" : { "PB-1" : 1, "PB-2" : 0, "LS-1" : 0, "SS-5" : 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!