The Industry Marketplace is an autonomous and decentralized platform for humans and machines to buy and sell services, data and goods. It combines the IOTA Tangle with standardized machine-readable contracts and an integrated decentralized identity system to enable actors to tender, bid and pay for services.
To tender, bid, or pay in Industry Marketplace buyer (the service requester) first request for a proposal for a good or service specifying the requirements using eCl@ss attributes. All the service providers in that marketplace receive the call for proposal and can send a proposal to the requester asking a price if it meets the requirements. After sending the proposal the service requester accepts or rejects the proposal.
A service provider can receive multiple calls for proposal at the same time but all the services are not same and one can be more profitable than others. If a service provider can determine the most profitable service it can gain more.
In this proof of the concept project, I will show how Industry Marketplace and eCl@ss can help your device to find the best client as a service provider to provide the most profitable goods or services. I will use the open-source Industry Marketplace Service App and Python language for my project.
To connect with Industry Marketplace, the Service App (nodejs based server) should be running in your server or device. In this project, I will use Raspberry Pi to host the service app as well as run the client program.
The main work is in step 7. If you have a working setup for Industry Marketplace you can directly go to step 7.
Getting Started with Raspberry PiI am assuming you have some previous experience of working with raspberry pi, Putty, and Python. If not, you should read some getting started tutorial before further proceeding this project.
Step 1: Choosing the right version of Raspberry Pi and OSThe technical documentation of Industry Marketplace recommends Raspberry Pi 3 B+ or higher but with my limited knowledge, I could not run the hole apps successfully on Raspberry Pi 3 B+. After several fail steps, I succeeded to get everything worked in Raspberry Pi 4, 4GB version with Raspbian Buster with desktop OS. You can download the OS from here.
Step 2: Installing Nodejs and Yarn to the PiFor running the service app Nodejs 10 or higher is required. To install Nodejs 10 in your Pi run the following commands in the terminal of Raspberry Pi :
curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
sudo apt-get install nodejs
Verify the node is successfully installed and working with node -v
command (as of this writing, I get 10.20.0).
Yarn is a new package manager for node.js. It is a common project developed by such companies as Facebook, Exponent, Google, and Tilde. Yarn is more stable and faster than NPM. Use the following commands to install yarn in Pi.
Install the Yarn dependency manager, which we’ll use to run our app:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
Run yarn -v
to verify (Version 1.22.4 as of today)
To get the latest copy of Industry Marketplace Server App clone the GitHub repo using the following command:
git clone https://github.com/iotaledger/industry-marketplace.git marketplace
This command will download the Industry Marketplace App inside marketplace directory.
Check the files in directory using:
cd marketplace
ls
You will get the list of all files in downloaded in the directory:
So, the service app is successfully downloaded. Our next step is to download the client application. But before that we to try a quick check to sure that so far everything is working.
Step 4: Run the ServiceApp for a checkGo to ServiceApp directory and run the command:
cd ServiceApp
yarn run dev
This command will download all the dependencies and run the service app server.
Wait for a couple of minutes until you see the following output on the terminal.
If your Pi is connected to a monitor you will find a browser window automatically opened and get the following view.
If Raspberry Pi is not connected to a monitor you can access the server through the ip address of the Pi from any browser from the same network. In the browser tab type ip_address:3000. You will get the following output from the browser.
If you get this then a big Congratualtion!!! So far everything is working perfectly. In the next step we will connect the client app to the server. Keep following...
Step 5: Downloading Python-Helper Client LibraryClone the python client library to a directory named helper using the following command. You can change the directory name if you want.
git clone https://github.com/iota-community/industry-marketplace-python-helper.git helper
Check the following files are downloaded.
To run the python sample program we will create a Python3 virtual environment. Use the following commands to create and activate a Python3 virtual environment to the home directory of your Pi.
python3 -m venv ~/my_venv
source ~/my_venv/bin/activate
If successful you will see the result
Using pip install all the python 3 requirements with the following commands:
pip install -r requirements.txt
Wait for a moment while installing the packages...
When completed run the service_requester sample app came with the client library using the command below...
You will get the connection confirmation with the server app. But before that be sure server app is running in another terminal window.
You will also get a response from the service app window after connecting to the client.
Step 6: Checking Service Provider & Service Requester interactionDownload two copy of Industry Marketplace Service App by cloning github repo. Type the following commands:
git clone --depth=1 https://github.com/iotaledger/industry-marketplace.git provider
git clone --depth=1 https://github.com/iotaledger/industry-marketplace.git requester
The above commands save one copy of the app to provider directory and one copy to requester directory. Now we will run both the app. If you like to run both apps from the same device you need to change the port number for one app. Let's do it for the provider app. Run the following command from provider directory:
git apply ../helper/patches/different_ports.patch
Go to the directories from two different windows and run the app as you did in Step 4.
Go to helper directory and run service_requester.py and service_provider.py from two different windows as you did in step 5.
Now, go to the requester from the browser and make a manual service request like this:
From the terminal you will get "received call for proposal message" with the irdi.
If you now open the service provider tab from the browser you will see a proposal received from the requester. From here you can put a prise in IOTA token and send the request to the requester.
After sending the proposal the proposal will be received by the requester and the requester and either accept or reject the proposal.
The service provider will be informed if the requester accepts or rejects the proposal.
On fulfillment of the service, the requester can proceed with the payment.
The whole process will be completed by the completion of the payment.
All the steps and the related information can also be observed from the terminal as shown in the following screenshots.
Both the web client and the python program can work independently and do not depend on one another. But the service app server must be running.
So far we just check our tools and all are ready. Now let us use the tools and make something as our requirement.
7. Giving your Device the Magic PowerAs you follow all the previous steps successfully, you are ready to show your creativity and now you have the ability to give your device superpower. In this step, I am going to show you how you can make your device/machine capable to choose the right deal (the most profitable service request) from the market.
In industry marketplace there are two parties (device/machine). One act as a service requester, the device which needs data or service. Another party acts as a service provider. A service provider can have the capability of providing multiple services but all the services may not equally profitable.
When a service provider (SP) receives multiple work requests at the same time it should select the most suitable and most profitable request for sending a proposal. So for providing the right service, a service provider should have the capability to understand all the eCl@ss attributes correctly and it should also know how to calculate the asking price and profit. For reading the attributes of service correctly eCl@ss irdi are used. For calculating the asking price and profit I am just using some random equation. Definitely the rial scenario is more complex than that.
For reading the eCl@ss attributes for some specific services I added few extra method in the imp.py file provided in the industry marketplace python helper github ripo.
#######################################Added##########################################
def get_price(self, irdi, submodels):
'''
Get the price for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'price'][0]
except IndexError:
return None
def get_location(self, irdi, submodels):
'''
Get the service location for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'location [lat, lng]'][0]
except IndexError:
return None
def get_total_weight(self, irdi, submodels):
'''
Get the cell tower range for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'total weight (freight) [kg]'][0]
except IndexError:
return None
def get_starting_point(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'starting point [lat, lng]'][0]
except IndexError:
return None
def get_destination(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'destination [lat, lng]'][0]
except IndexError:
return None
def get_number_of_photo(self, irdi, submodels):
'''
Get the energy consumption of BTS for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'number of photos that can be stored'][0]
except IndexError:
return None
def get_reliability_duration(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'reliability duration [min]'][0]
except IndexError:
return None
def get_target_location(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'target location [lat, lng]'][0]
except IndexError:
return None
def get_autonomous(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'autonomous'][0]
except IndexError:
return None
def get_max_persons(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'max. number of persons'][0]
except IndexError:
return None
def get_duration(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'duration[min]'][0]
except IndexError:
return None
def get_max_valocity(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'maximum velocity at rated value [km/h]'][0]
except IndexError:
return None
def get_max_radius(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'max. monitoring radius [m]'][0]
except IndexError:
return None
def get_2_4_value(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == '2,4 GHz'][0]
except IndexError:
return None
def get_5_value(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == '5 GHz'][0]
except IndexError:
return None
def get_energy_consumption(self, irdi, submodels):
'''
Get the cell tower frequency for a irdi from the submodels
'''
try:
return [x['value'] for x in submodels.values() if x['idShort'] == 'energy consumption [kW/h]'][0]
except IndexError:
return None
####################################################################################################
Now, let's go for the service provider-client program. This is the main program you run on your device for checking and responding on call for proposal. For this demo project, my main goal is to choose the most profitable proposal call from multiple service requests and send proposal for the most profitable request. For the demo code, I am waiting for three proposals call before sending any proposal. Waiting can also be based on time like, before sending a proposal a service requester will wait up to 5 minutes and will determine the best call from all the calls received on that five minutes.
After getting every request I am checking either my device is capable to provide that service maintaining all the attributes mentioned in the request. If it does not meet any criteria the proposal request is ignored. If it meets all requirements then the proposal data is stored in a text file setting irdi as the file name.
The device than calculate the price for the service considering the mentioned attributes. It also calculates the profit for the service. After that irdi, calculated price and the profit is stored in three separate lists.
"""Reads the irdi attributes from the call for proposal and check the capability and calculate price."""
starting_point = self.get_starting_point(irdi, submodels)
destination = self.get_destination(irdi, submodels)
total_weight = self.get_total_weight(irdi, submodels)
"""This portion is used to check the capability of providing service mentioned to the call for proposal
and return if does not meet."""
if total_weight>5:
return #assuming it only can carry 5 Kg
"""This portion is used to write the received data and written to a text file with irdi number so
that it can be used when sending proposal."""
with open(irdi + '.txt', 'w') as json_file:
json.dump(data, json_file)
"""After getting starting point and destination I am calculating distance and from that distance
and weight I am calculating the price just using a random equation. I also calculating the profit
assuming 35% of the total price."""
lat1 = starting_point.split(',')[0]
lon1 = starting_point.split(',')[1]
lat2 = destination.split(',')[0]
lon2 = destination.split(',')[1]
dlon = float(lon2) - float(lon1)
dlat = float(lat2) - float(lat1)
a = (sin(float(dlat) / 2)) ** 2 + cos(float(lat1)) * cos(float(lat2)) * (sin(float(dlon) / 2)) ** 2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = self.R * c
price = distance * 1.2 + total_weight * 1.2
self.log('price')
self.log(price)
profit = 0.35 * price
self.irdi_list.append(irdi)
self.price_list.append(price)
self.profit_list.append(profit)
print('Received proposal request to carry = %s Kg for %s Km, for irdi %s' % (
total_weight, distance, irdi))
"""Wait for three proposal requests before sending the proposal to determine most profitable one."""
if len(self.irdi_list) >= 3:
self.sent_proposal()
On every proposal request received, the device checks the number of total proposals received by it. If it is three or more than three then the proposal request for the maximum profit is determined form the profit list.
After determining the profitable request all the related information (irdi, price, data, submodel) for that request is read, and the device sends the proposal to that specific requester.
def sent_proposal(self):
"""This function is used to identify the most profitable request for the multiple requesters
and send the proposal to that request only."""
index_max = self.profit_list.index(max(self.profit_list))
proposed_irdi = self.irdi_list[index_max]
proposed_price = int(self.price_list[index_max])
with open(proposed_irdi + '.txt') as json_file:
data = json.load(json_file)
try:
ret = self.proposal(data, price_in_iota = proposed_price)
except Exception as e:
self.log('Unable to send proposal', e)
self.log('proposal sent! Requesting %si for this service' % self.proposed_price)
If the proposal is rejected by the service requester than the next profitable request can be considered or the device can offer a discount which one is profitable.
Comments