Skip to Content

Part 4: Controlling the manufacturing process in real time

 

Link to part 1 of the Blog Series – https://blogs.sap.com/2018/06/19/iot-prototype-with-business-rules-and-workflow-blog-series-14/

Link to part 2.1  of the Blog Series – https://blogs.sap.com/2018/06/28/iot-prototype-with-business-rules-and-workflow-blog-series-24/

Link to part 2.2 of the Blog Series – https://blogs.sap.com/2018/07/04/iot-prototype-with-business-rules-and-workflow-blog-series-2.24/

Link to part 3 of the Blog Series – https://blogs.sap.com/2018/07/04/iot-prototype-with-business-rules-and-workflow-blog-series-34/

The previous two scenarios gave us an idea about how the data is sent from the sensors to the IoT platform on SCP. This final scenario details how the data can be sent to actuators from the IoT platform. All IoT scenarios we developed in this blog series received data from the sensors and either took action automatically or sent a message to the user’s inbox.  But this scenario will receive data from the sensors (a sensor is a device, module, or subsystem whose purpose is to detect events or changes in its environment and send the information to other electronics, frequently a computer processor) and sends action to the actuators (an actuator is a component of a machine that is responsible for moving and controlling a mechanism or system, for example by opening a valve) in the manufacturing process.

Manufacturing process considered in the scenario:

The manufacturing process considered for this scenario is responsible for checking the thickness of the metal sheets produced by the machine. The thickness of the metal sheet is set to a value by the business and if the thickness varies from that value, the manufacturing process has to be stopped automatically. I agree this is too simple for a real scenario but implementing this scenario as a prototype would give us a clear understanding about how the data can be sent from the IoT platform to the device.

We can make small modifications to the prototype and accommodate it to a slightly complex scenario where the metallic sheets can be diverted to a different conveyer belt depending on the thickness of the sheet. According to the business rules set by the user, data can be sent to the appropriate actuators to divert the sheets to different conveyer belts depending on the value of thickness received from the sensor.

 

Steps to complete the scenario

Step 1- Device management: Set up message types, device types and device in Internet of things cockpit. => This step has been completed in the first part of the Blog series for all three scenarios.

The following 2 steps will be covered in this blog.

Step 2 – Business Rules: Create business rule services and set up rules.

Step 3 – IoT application: Create Java application to integrate all these services and Python code to send the sensor data from the device.

Make sure that the following services are enabled in SAP Cloud Platform Neo environment under your trial account.

·        Internet of Things

·        Business Rules

 

Step 2: Business Rules

Let us configure the business rules for this scenario.

Go to Business Rule Editor – https://bpmruleseditor-s000xxxxxxxtrial.dispatcher.hanatrial.ondemand.com/index.html

Create a project to hold the business rule service for the scenario.

 

Create two Data Objects

Input: with one attribute – Thickness

 

Output: with one attribute – Action

Create a Rule.

ThicknessRule – This rule is constructed as a Decision Table. Depending on the thickness received by the rule different action command is sent as output.

 

Create a RuleSet

ThicknessRuleSet – Add the Rule to the Rule set.

Create a Rule Service

ThicknessRulesService – This rule service takes the data object ‘Input’ as input and returns ‘Output’ as result.

Make sure that this rule service is assigned to the rule set. Activate all the objects created in the steps above. Do not forget to deploy the rule service.

The rule service will be called from the IoT application to get the proper action for the thickness received from the sensors.

You can test the Business Rule Services by using Postman as shown in the part 2.1 of the blog series.

Step 3: IoT Application

Steps to receive data from MMS of IoT to the device

Since we use HTTP for communicating between the device and IoT platform, the data cannot be pushed directly from IoT into the device. It is done in two steps:

a. Use Push Service to push the data from the IoT application to MMS. MMS stores these messages in the data store. These messages are pushed with a particular device ID.

b. Push messages must be retrieved actively by a device by supplying a GET request.

 

If you use WebSockets for communication, this can be achieved in a single step. Please refer this link for more details – https://help.sap.com/viewer/7436c3125dd5491f939689f18954b1e9/Cloud/en-US/9da1c18f6ab947c58052f4d07498a654.html

 

Step a, mentioned above is code in the IoT application (Java) and Step b is coded in the device (Python)

Python code snippet written in Raspberry Pi

Raspberry PI set up

In order to simulate this scenario, I have set up my Raspberry Pi with a button – on click of which the thickness information is sent to SCP, an active buzzer and a LED to act as actuators. If the output of the business rules sends an action to the actuator, a LED lights up and buzzer sounds an alarm in my Raspberry Pi. 

The thickness sensor sends the values to SCP using the code below. It also includes the code for pulling the data from MMS in case there is an action required based on the thickness value sent.

 Note that you would require the following values from the IoT cockpit to send sensor values from the device to SCP. 

SCP account ID, Host name, Device ID, message type id, Authorization token for the device.

http = urllib3.PoolManager()
headers = urllib3.util.make_headers(user_agent=None)
headers['Authorization'] = 'Bearer ' + config.oauth_credentials_for_device
headers['Content-Type'] = 'application/json;charset=utf-8'
url='https://iotmms' + config.hcp_account_id + config.hcp_landscape_host + '/com.sap.iotservices.mms/v1/api/http/data/'+ str(config.device_id)
urllib3.disable_warnings()


GPIO.output(lightPin,False)
GPIO.output(buzzPin,GPIO.LOW)
try:
    
    while True:

        if GPIO.input(buttonPin) == False:
#Get thickness from user. This value will come from sensor in live scenario
            thickness = input("Enter thickness:")
            time = datetime.datetime.now().time()
            date = datetime.datetime.now().date()
            body= '{"mode":"async", "messageType":"' + str(config.message_type_id_THICK) + '", "messages":[{"Date":'+ ' "' + str(date) + '"'+ ',"Time":'+ ' "' + str(time) + '"'+ ',"Thickness":'+ ''+str(thickness)+' }]}'
            r = http.urlopen('POST', url, body=body, headers=headers)
#wait for 5 seconds before polling the data from MMS.            
	    sleep(5)
            message = http.urlopen('GET', url, headers=headers)
#if the message is not empty - [], then light up the LED and switch the buzzer on            
	    if len(message.data) > 2:
                GPIO.output(buzzPin,GPIO.HIGH)
                GPIO.output(lightPin,True)
          
except KeyboardInterrupt:
        GPIO.output(lightPin,False)
        GPIO.cleanup()

IoT application

This is the ‘glue’ application which integrates Step 1 and Step 2. The application performs the following steps.

a. Read the thickness value sensor from the device [use the device ID that we got while registering the device in Step 1.

b. Send the thickness value to Business rules to get the action that needs to be taken, if any. If the value of the action is ‘Stop’, push the message to the MMS with the device ID and the message ID (the direction of this message must be set to ‘To Device’ in the Device management step – Step 1 otherwise the message will not be pushed to the MMS/device)

Code snippet for Step a.

The thickness data which comes from the Raspberry Pi is written into table T_THICK_IOTMESSAGES as per the service mappings which were done towards the end of Step 1.

Note: if you have not done the process service mappings, then use the default message table name created by the IoT device.

 

       /**
	 * read data from device
	 * 
	 * @return
	 */
	private String getThicknessfromDevice(String deviceid) {
		InitialContext ctx = null;
		DataSource ds = null;
		Connection con = null;
		String result = null;
		try {
			ctx = new InitialContext();
			ds = (DataSource) ctx.lookup("java:comp/env/jdbc/default");
			con = ds.getConnection();
			PreparedStatement query = con.prepareStatement(
					"SELECT  TOP 1 \"C_THICKNESS\" FROM \"SYSTEM\".\"T_THICK_IOTMESSAGES\" WHERE \"G_DEVICE\" = '"+deviceid+"' ORDER BY \"G_CREATED\" desc");
			ResultSet rs = query.executeQuery();
			while (rs.next()) {
				result = rs.getString(1);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;

	}

Code snippet for Step b.

The code lines below send the thickness read from Step a. to the Business Rule Engine to run it through the rules.

/**
	 * Check thickness against rules
	 * @param materialtype
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public String checkThickness(String thickness)
			throws ClientProtocolException, IOException {
		HttpContext httpContext = new BasicHttpContext();
		httpContext.setAttribute(HttpClientContext.COOKIE_STORE, new BasicCookieStore());

		HttpPost httpPost = null;
		CloseableHttpResponse response = null;
		CloseableHttpClient httpClient = null;
		try {
			httpClient = getHTTPClient();
			String rulesRuntimeUrl = "https://bpmrulesruntimebpm-s000xxxxtrial.hanatrial.ondemand.com/";
			String xsrfTokenUrl = rulesRuntimeUrl + "rules-service/v1/rules/xsrf-token";
			String invokeUrl = rulesRuntimeUrl
					+ "rules-service/v1/rules/invoke?rule_service_name=Thickness::ThicknessRulesService";
			httpPost = new HttpPost(invokeUrl);

			httpPost.addHeader("Content-type", "application/json");
			String xsrfToken = getXSRFToken(xsrfTokenUrl, httpClient, httpContext);
			if (xsrfToken != null) {
				httpPost.addHeader("X-CSRF-Token", xsrfToken);
			}
			// replace value of authorizationHeader with base64 encoded value of
			// “<user-name>:<password>”
			String authorizationHeader = "<base64 encoded value of <user-name>:<password>>";
			httpPost.addHeader("Authorization", "Basic " + authorizationHeader);

			// construct input data to the rules service
			String fact = "{ \"__type__\":\"Input\",\"Thickness\":\"" + thickness + "\"}";
			StringEntity stringEntity = new StringEntity(fact);
			httpPost.setEntity(stringEntity);

			response = httpClient.execute(httpPost, httpContext);
			// process your response here
			ByteArrayOutputStream bytes = new ByteArrayOutputStream();
			InputStream inputStream = response.getEntity().getContent();
			byte[] data = new byte[1024];
			int length = 0;
			while ((length = inputStream.read(data)) > 0) {
				bytes.write(data, 0, length);
			}
			String respBody = new String(bytes.toByteArray(), "UTF-8");
			// The respBody is a JSON and parse is to get discount
			//return respBody;
			ObjectMapper objectMapper = new ObjectMapper();
			JsonNode jsonObject = objectMapper.readValue(respBody, JsonNode.class);
			String action = jsonObject.get("Action").asText();
			if (action.equalsIgnoreCase("Stop")) {
				//Push raise alarm message to the device
				return pushMessageToDevice(thickness);
			}else{
				return "No action required";
			
			}
		} finally {
			if (httpPost != null) {
				httpPost.releaseConnection();
			}
			if (response != null) {
				response.close();
			}
			if (httpClient != null) {
				httpClient.close();
			}
		}
	}
	
	
	/**
	 * Gets the xsrf token for business rules/workflow
	 * 
	 * @param requestURL
	 * @param client
	 * @param httpContext
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	private String getXSRFToken(String requestURL, CloseableHttpClient client, HttpContext httpContext)
			throws ClientProtocolException, IOException {
		HttpGet httpGet = null;
		CloseableHttpResponse response = null;
		String xsrfToken = null;
		String authorizationHeader = null;
		try {
			httpGet = new HttpGet(requestURL);
			// replace value of authorizationHeader with base64 encoded value of
			// “<user-name>:<password>”
			authorizationHeader = "<base64 encoded value of <user-name>:<password>>";
			httpGet.addHeader("Authorization", "Basic " + authorizationHeader);
			httpGet.addHeader("X-CSRF-Token", "Fetch");
			response = client.execute(httpGet, httpContext);
			// Fetch the token from response and return
			Header xsrfTokenheader = response.getFirstHeader("X-CSRF-Token");
			if (xsrfTokenheader != null) {
				xsrfToken = xsrfTokenheader.getValue();
			}
		} finally {
			if (httpGet != null) {
				httpGet.releaseConnection();
			}
			if (response != null) {
				response.close();
			}
		}
		return xsrfToken;
	}

 

Code to push the message to the MMS/device is as below

/**
   * 
   * @param thickness
   * @return
   * @throws ClientProtocolException
   * @throws IOException
   */
	public String pushMessageToDevice(String thickness) throws ClientProtocolException, IOException {
		HttpContext httpContext = new BasicHttpContext();
		httpContext.setAttribute(HttpClientContext.COOKIE_STORE, new BasicCookieStore());

		HttpPost httpPost = null;
		CloseableHttpResponse response = null;
		CloseableHttpClient httpClient = null;
		try {
			httpClient = getHTTPClient();
			String iotUrl = "https://iotmmss000xxxxtrial.hanatrial.ondemand.com/com.sap.iotservices.mms/";
			String pushUrl = iotUrl
					+ "v1/api/http/push/"+deviceID;
			httpPost = new HttpPost(pushUrl);

			httpPost.addHeader("Content-type", "application/json");
			// replace value of authorizationHeader with base64 encoded value of
			// “<user-name>:<password>”
			String authorizationHeader = "<base64 encoded value of <user-name>:<password>>";
			httpPost.addHeader("Authorization", "Basic " + authorizationHeader);

			// construct input data to the rules service
			String fact = "{ \"method\": \"http\",\"Sender\": \"IoTApplication\",\"messageType\": \""+messageID+"\",\"messages\":[{\"Date\":"+"12122018"+",\"Time\":\""+111111+"\",\"Thickness\":"+thickness+" }]}";
			StringEntity stringEntity = new StringEntity(fact);
			httpPost.setEntity(stringEntity);
			response = httpClient.execute(httpPost, httpContext);
			// process your response here
			ByteArrayOutputStream bytes = new ByteArrayOutputStream();
			InputStream inputStream = response.getEntity().getContent();
			byte[] data = new byte[1024];
			int length = 0;
			while ((length = inputStream.read(data)) > 0) {
				bytes.write(data, 0, length);
			}
			String respBody = new String(bytes.toByteArray(), "UTF-8");
			// The respBody is a JSON and parse is to get discount
				return respBody;
			
			
		} finally {
			if (httpPost != null) {
				httpPost.releaseConnection();
			}
			if (response != null) {
				response.close();
			}
			if (httpClient != null) {
				httpClient.close();
			}
		}
		
	}

 

The glue code which calls all these functions is as below.

response.getWriter().print(checkThickness(getThicknessfromDevice(deviceID)));

 

Do not forget to deploy the Java application in SCP.

I have uploaded the complete java application for Reorder scenario in the link below.

Java Code for the IoT Prototype – Thickness

 

Prototype in action

  1. Switch on the Raspberry Pi and press the button. Enter the value for thickness as input. The value is sent to SCP.

2. You can see the values getting stored in the table which is configured in the Service Mappings – T_THICK_IOTMESSAGES.

 

3. Run the Java servlet with the glue code. In an ideal prototype this part will be scheduled to run in the background. In this prototype, it reads the most recent value from the table and takes action accordingly.

URL – https://iotapplications000XXXXtrial.hanatrial.ondemand.com/IoTApplication/checkThickness

You will see that the message has been pushed to the device. This is because the value of thickness received is 12.1 which is more than the set value of 12 (check business rules).

4. Raspberry Pi pulls this message from the MMS using a Get request and you can see the message being received.

5. The LED and the buzzer are turned on.

This completes the blog series. Feel free to get in touch with me if you have questions.

 

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Ashutosh Shrivastava

    Hi Shradha,

    Nice blog. Definitely i will try this.

    Currently i am struggling with  one issue hope you can look in to this.

    So Requirement is to consume sensor data (DHT 11- reads temp/humidity data) in to HCP-IoT tables.

    Steps i have performed so far–

    1> After completing all basic steps (creation of schema/DB, iotmms deployment, creation of Message type, Device type) configured message management in HCP iot. (please see attached pic).

    2> I have written one program in Python3 on raspberry pi. ( please see attached program) This program will first make connection with HCP Iot and then pass the temperature and humidity in display stored messages in HCP iot. however this program is not able to make connection. but program is only able to print temperature and humidity on raspberry pi .

    Attaching code and MQTT broker point for your reference.

    I am not stick with MQTT . if any other possible solution is there please suggest.

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    
    
    # Import the Modules
    import sys
    import os
    import time
    import paho.mqtt.client as mqtt
    
    def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))
    
    client = mqtt.Client()
    
    #Set username and pw here
    
    client.username_pw_set("iot",password="iot")
    client.on_connect = on_connect
    
    #add the url or ip to your mqtt broker port 1883 is the default non
    encrypted port
    
    client.connect("192.168.1.4", 1883, 60)
    
    
    # print temperature
    
    import Adafruit_DHT
    import time
    als = True
    while als:
            humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.DHT11,4)
            if humidity is not None and temperature is not None:
                    humidity = round(humidity, 2)
                    temperature = round(temperature,2)
                    print 'Temperature = {0:0.1f}*C Humidity =
    {1:0.1f}%'.format(temperature, humidity)
                    time.sleep(60)
    sys.exit(0)
    

    Thanks,

    Ashutosh

    (0) 

Leave a Reply