Skip to content

Commit

Permalink
Add files for v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
safarsaitam committed Sep 23, 2021
1 parent 3357668 commit 57d5fc0
Show file tree
Hide file tree
Showing 318 changed files with 7,051 additions and 2,024 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6
FROM python:3.6-slim

RUN mkdir -p usr/src/dinasore-ua

Expand Down
61 changes: 30 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
![logo](https://github.com/DIGI2-FEUP/dinasore/wiki/images/logo.png)
![logo](resources/images/logo.png)

**D**ynamic **IN**telligent **A**rchitecture for **S**oftware and M**O**dular **RE**configuration - **DINASORE** - is a distributed platform that runs at the
fog computing level, enabling the pre-processing of data using algorithms, that are encapsulated inside modules (function blocks).

**D**ynamic **IN**telligent **A**rchitecture for **S**oftware and M**O**dular **RE**configuration - **DINASORE** - is a distributed platform that runs at the fog computing level, enabling the pre-processing of data using algorithms, that are encapsulated inside modules, i.e. Function Blocks (FBs).
The principal advantage of this platform is the redistribution of the running modules across a distributed fog network.
So the user can develop their own code, in Python, and them upload it to the different DINASORE nodes in the network.
To draw the system it's used the 4DIAC-IDE witch is according the IEC61499 standards.

The principal advantage of this platform is the redistribution of the running modules across a **Cyber-Physical System** (CPS). So the user can develop their own code, in Python, and then upload it to the different DINASORE nodes in the CPS. To draw the system it's used the **4DIAC-IDE** which is according the **IEC61499** standards. This version is targeted to the **Industry4.0** applications, for that, it also uses the **OPC-UA** protocol to allow communication with the other industrial components.
This version is targeted to the Industry4.0 applications, for that it was also used the UPC-UA protocol to allow the communication with the other industrial components.

## Content

* [Home Wiki](https://github.com/DIGI2-FEUP/dinasore/wiki)
* [Install](https://github.com/DIGI2-FEUP/dinasore/wiki/1.-Install)
* [Function Blocks and 4DIAC-IDE](https://github.com/DIGI2-FEUP/dinasore/wiki/2.-Function-Blocks-and-4DIAC)
* [Build new Function Blocks](https://github.com/DIGI2-FEUP/dinasore/wiki/2.1.-Build-new-Function-Blocks)
* [OPC-UA Data Model](https://github.com/DIGI2-FEUP/dinasore/wiki/2.3.-OPC-UA-Data-Model)
* [Behavioral Anomaly Detection](https://github.com/DIGI2-FEUP/dinasore/wiki/2.2.-Behavioral-Anomaly-Detection-functionality)
* [Tutorials Resume](https://github.com/DIGI2-FEUP/dinasore/wiki/3.-Tutorials-Resume)
* [Sensorization - "Hello World!"](https://github.com/DIGI2-FEUP/dinasore/wiki/3.1.-Hands-On:-Sensorization-"Hello-World!")
* [Sensorization - Distributed CPS](https://github.com/DIGI2-FEUP/dinasore/wiki/3.2.-Hands-On:-Distributed-Sensorization)
* [Optimization](https://github.com/DIGI2-FEUP/dinasore/wiki/3.3.-Hands-On:-Optimization)
* [Install](../../wiki/1.-Install)
* [Function Blocks and 4DIAC-IDE](../../wiki/2.-Function-Blocks-and-4DIAC)
* [Build new Function Blocks](../../wiki/4.-Build-new-Function-Blocks)
* [OPC-UA Data Model](../../wiki/2.3.-OPC-UA-Data-Model)
* [Behavioral Anomaly Detection](../../wiki/2.2.-Behavioral-Anomaly-Detection-functionality)
* Tutorials
* [Sensorization - "Hello World!"](../../wiki/3.1.-Hands-On:-Sensorization-%22Hello-World!%22)
* [Sensorization - Distributed CPS](../../wiki/3.2.-Hands-On:-Sensorization)
* [Optimization](../../wiki/3.3.-Hands-On:-Optimization)
* Use Cases
* [Painting Area Simulation](../../wiki/5.1.-Painting-Area-Simulation)
* [Assembly Area Simulation](../../wiki/4.2.-Assembly-Area-Simulation)
* [Sensor Actuator Control System](../../wiki/5.-Sensor-Actuator-Control-System)
* Industrial Sensorization using Modbus
* Universal Robots and 3D printed Gripper Control
* Anomaly Detection in a Servo Motor Robotic Arm



## Features
- [x] Communication between the DINASORE and the 4DIAC-IDE
Expand All @@ -28,23 +40,10 @@ The principal advantage of this platform is the redistribution of the running mo
- [x] Docker integration
- [x] Opc-Ua integration
- [x] Configuration storage
- [x] Test with complex variables (lists, arrays, methods (strings))


## Citations

For any publications or articles related with DINASORE, please use the following citation, or the BibTex entry (if you are using LaTex).

- E. Pereira, J. Reis, and G. Gonçalves, “DINASORE: A Dynamic Intelligent Reconfiguration Tool for Cyber-Physical Production Systems,” in Eclipse Conference on Security, Artificial Intelligence, and Modeling for the Next Generation Internet of Things (Eclipse SAM IoT), 2020, pp. 63–71, [Online]. Available: http://ceur-ws.org/Vol-2739/#paper_9.
- [ ] Kill all process
- [x] Update automatically the python and xml file (use os.stat())
- [ ] Download function blocks from 4DIAC-IDE repository
- [ ] Edit the function blocks in the 4DIAC-IDE and automatically update the code in the nodes
- [ ] Test with complex variables (lists, arrays, methods (strings))

```
@inproceedings{pereira2020dinasore,
title = {DINASORE: A Dynamic Intelligent Reconfiguration Tool for Cyber-Physical Production Systems},
author={Pereira, Eliseu and Reis, Joao and Gon{\c{c}}alves, Gil},
booktitle={Eclipse Conference on Security, Artificial Intelligence, and Modeling for the Next Generation Internet of Things (Eclipse SAM IoT)},
year={2020},
pages = {63--71},
url = {http://ceur-ws.org/Vol-2739/#paper_9}
}
```

2 changes: 0 additions & 2 deletions _config.yml

This file was deleted.

23 changes: 22 additions & 1 deletion core/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from core import fb_interface
from xml.etree import ElementTree as ETree
import logging

import inspect



Expand Down Expand Up @@ -59,6 +59,26 @@ def create_fb(self, fb_name, fb_type, monitor=False):
# check if if happened any importing error
if fb_definition is not None:

# Checking order and number or arguments of schedule function
# Logs warning if order and number are not the same
scheduleArgs = inspect.getargspec(fb_obj.schedule).args
if len(scheduleArgs) > 3:
scheduleArgs = scheduleArgs[3:]
scheduleArgs = [i.lower() for i in scheduleArgs]
xmlArgs = []
for child in fb_definition:
inputvars = child.find('InputVars')
varslist = inputvars.findall('VarDeclaration')
for xmlVar in varslist:
if xmlVar.get('Name') is not None:
xmlArgs.append(xmlVar.get('Name').lower())
else:
logging.error('Could not find mandatory "Name" attribute for variable. Please check {0}.fbt'.format(fb_name))

if scheduleArgs != xmlArgs:
logging.warning('Argument names for schedule function of {0} do not match definition in {0}.fbt'.format(fb_name))
logging.warning('Ensure your variable arguments are the same as the input variables and in the same order')

## if it is a real FB, not a hidden one
if monitor:
fb_element = fb.FB(fb_name, fb_type, fb_obj, fb_definition, monitor=self.monitor)
Expand All @@ -71,6 +91,7 @@ def create_fb(self, fb_name, fb_type, monitor=False):
return fb_element, fb_definition
else:
logging.error('can not create the fb type: {0}, instance: {1}'.format(fb_type, fb_name))
return None, None

def create_connection(self, source, destination):
logging.info('creating a new connection...')
Expand Down
38 changes: 37 additions & 1 deletion core/fb.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import threading
import logging
from core import fb_interface

from core import sniffer
import os
from data_model_fboot import utils
import queue

class FB(threading.Thread, fb_interface.FBInterface):

Expand All @@ -13,18 +16,39 @@ def __init__(self, fb_name, fb_type, fb_obj, fb_xml, monitor=None):
self.kill_event = threading.Event()
self.execution_end = threading.Event()
self.ua_variables_update = None
self.update_variables_fboot = None
self.fb_type = fb_type

if fb_type != 'TEST_FB' and fb_name != 'START':
# Gets the dir path to the py and fbt files
root_path = utils.get_fb_files_path(fb_type)
# Gets the file path to the python file
py_path = os.path.join(root_path, fb_type + '.py')
message_queue = queue.Queue()
self.message_queue = message_queue
self.sniffer_thread = sniffer.Sniffer(fb_type, py_path, message_queue)
self.sniffer_thread.start()

def run(self):
logging.info('fb {0} started.'.format(self.fb_name))

while not self.kill_event.is_set():

if self.fb_type != 'TEST_FB':
try:
self.fb_obj = self.message_queue.get(False)
logging.info('Updated {0}'.format(self.fb_type))
except queue.Empty:
pass

# clears the event when starts the execution
self.execution_end.clear()

self.wait_event()

if self.kill_event.is_set():
if self.fb_type != 'TEST_FB':
self.sniffer_thread.kill()
break

inputs = self.read_inputs()
Expand Down Expand Up @@ -54,11 +78,21 @@ def run(self):
if self.kill_event.is_set():
break

if outputs is None:
logging.error('Outputs are null, please check {0}.py'.format(self.fb_name))
# Stops the thread
logging.info('stopping the fb work...')
break

self.update_outputs(outputs)

# updates the opc-ua interface
if self.ua_variables_update is not None:
self.ua_variables_update()

if self.update_variables_fboot is not None:
self.update_variables_fboot()


# sends a signal when ends execution
self.execution_end.set()
Expand All @@ -78,3 +112,5 @@ def stop(self):

logging.info('fb {0} stopped.'.format(self.fb_name))

if self.fb_type != 'TEST_FB':
self.sniffer_thread.kill()
46 changes: 33 additions & 13 deletions core/fb_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,33 +245,53 @@ def __init__(self, fb_name, fb_type, xml_root, monitor=None):
if interface.tag == 'EventInputs':
# Iterates over the input events
for event in interface:
event_name = event.attrib['Name']
event_type = event.attrib['Type']
self.input_events[event_name] = (event_type, None, False)
try:
event_name = event.attrib['Name']
event_type = event.attrib['Type']
except KeyError:
logging.error('Could not find mandatory attributes "Name" and "Type"')
logging.error('Please check {0}.fbt for mistakes'.format(fb_name))
else:
self.input_events[event_name] = (event_type, None, False)

# Output Events
elif interface.tag == 'EventOutputs':
# Iterates over the output events
for event in interface:
event_name = event.attrib['Name']
event_type = event.attrib['Type']
self.output_events[event_name] = (event_type, None, False)

try:
event_name = event.attrib['Name']
event_type = event.attrib['Type']
except KeyError:
logging.error('Could not find mandatory attributes "Name" and "Type"')
logging.error('Please check {0}.fbt for mistakes'.format(fb_name))
else:
self.output_events[event_name] = (event_type, None, False)

# Input vars
elif interface.tag == 'InputVars':
# Iterates over the input vars
for var in interface:
var_name = var.attrib['Name']
var_type = var.attrib['Type']
self.input_vars[var_name] = (var_type, None, False)
try:
var_name = var.attrib['Name']
var_type = var.attrib['Type']
except KeyError:
logging.error('Could not find mandatory attributes "Name" and "Type"')
logging.error('Please check {0}.fbt for mistakes'.format(fb_name))
else:
self.input_vars[var_name] = (var_type, None, False)

# Output vars
elif interface.tag == 'OutputVars':
# Iterates over the output vars
for var in interface:
var_name = var.attrib['Name']
var_type = var.attrib['Type']
self.output_vars[var_name] = (var_type, None, False)
try:
var_name = var.attrib['Name']
var_type = var.attrib['Type']
except KeyError:
logging.error('Could not find mandatory attributes "Name" and "Type"')
logging.error('Please check {0}.fbt for mistakes'.format(fb_name))
else:
self.output_vars[var_name] = (var_type, None, False)

# Doesn't expected interface
else:
Expand Down
39 changes: 30 additions & 9 deletions core/fb_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,40 @@
import sys
import xml.etree.ElementTree as ETree
import logging
from data_model_fboot import utils


class FBResources:

def __init__(self, fb_type):
self.fb_type = fb_type

# Gets the dir path to the py and fbt files
self.root_path = utils.get_fb_files_path(fb_type)

# Gets the file path to the python file
self.py_path = os.path.join(os.path.dirname(sys.path[0]),
'resources',
'function_blocks',
fb_type + '.py')
self.py_path = os.path.join(self.root_path, fb_type + '.py')

# Gets the file path to the fbt (xml) file
self.fbt_path = os.path.join(os.path.dirname(sys.path[0]),
'resources',
'function_blocks',
fb_type + '.fbt')
self.fbt_path = os.path.join(self.root_path, fb_type + '.fbt')

def import_fb(self):
logging.info('importing fb python file and definition file...')
root = None
fb_obj = None

try:
concatenate = False
package = ''
for dir in self.root_path.split(os.sep):
if not concatenate:
if dir == 'resources':
concatenate = True
if concatenate:
package += dir + '.'
package = package[:-1]
# Import method from python file
py_fb = importlib.import_module('.' + self.fb_type, package='resources.function_blocks')
py_fb = importlib.import_module('.' + self.fb_type, package=package)
# Gets the running fb method
fb_class = getattr(py_fb, self.fb_type)
# Instance the fb class
Expand Down Expand Up @@ -58,6 +65,20 @@ def import_fb(self):
logging.info('fb definition (xml) imported from: {0}'.format(self.fbt_path))
logging.info('python file imported from: {0}'.format(self.py_path))

# Checking specified data types
for event in tree.findall('.//Event'):
if event.get('Type') is not None and event.get('Type') != 'Event':
logging.error('Wrong data type "{0}" specified for event {1}'.format(event.get('Type'), event.get('Name')))
logging.error('Defaulting to Event')
event.set('Type', 'Event')

for varDec in tree.findall('.//VarDeclaration'):
if varDec.get('Type') is not None and varDec.get('Type') not in utils.UA_TYPES:
logging.error('Unknown data type "{0}" assigned to variable {1}'.format(varDec.get('Type'), varDec.get('Name')))
logging.error('Defaulting to String')
varDec.set('Type', 'String')


return root, fb_obj

def get_xml(self):
Expand Down
2 changes: 1 addition & 1 deletion core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
# creates the 4diac manager
m = manager.Manager(monitor=monitor)
# sets the ua integration option
m.build_ua_manager(address, port_opc, 'data_model.xml')
m.build_ua_manager_fboot(address, port_opc)

# creates the tcp server to communicate with the 4diac
hand = tcp_server.TcpServer(address, port_diac, 10, m)
Expand Down
Loading

0 comments on commit 57d5fc0

Please sign in to comment.