Tuesday, 25 January 2022

OPC UA (A new standard Protocol for M2M)

Standard

What is OPC?

OPC: Open Platform Communication:

OPC is the interoperability standard for the secure and reliable exchange of data in the industrial automation space and in other industries. It is platform independent and ensures the seamless flow of information among devices from multiple vendors. The OPC Foundation is responsible for the development and maintenance of this standard.

Initially, the OPC standard was restricted to the Windows operating system. As such, the acronym OPC was borne from OLE (object linking and embedding) for Process Control. These specifications, which are now known as OPC Classic, have enjoyed widespread adoption across multiple industries, including manufacturing, building automation, oil and gas, renewable energy and utilities, among others.

When the standard was first released in 1996, its purpose was to abstract PLC specific protocols (such as Modbus, Profibus, etc.) into a standardized interface allowing HMI/SCADA systems to interface with a “middle-man” who would convert generic-OPC read/write requests into device-specific requests and vice-versa. As a result, an entire cottage industry of products emerged allowing end-users to implement systems using best-of-breed products all seamlessly interacting via OPC.

Communication Architecture before & after OPC:



For. e.g.








[For more, Reference: What is OPC? - OPC Foundation]


Various OPC Standards & their functions:


When examining the standards of OPC, OPC server / client terms should also be emphasized. OPC server (server); is a software application designed to work with one or more OPC features. OPC servers can be thought of as interpreters connecting the OPC environment with the devices’ local communication protocols or interfaces.

The task of the OPC server is to receive or send information to the device according to the commands of the OPC client. If it is an OPC client; are software used by an application to communicate with any compatible OPC server. OPC clients can be considered as a data-sink, as they carry out the initiation and control of communication with OPC servers. The OPC client sends communication requests to the OPC server.

Let's understand the data transmission between OPC Client & Server diagrammatically.



When data is returned from the OPC server, the OPC client converts this data to the local format in the application to be used; Thus, correct operation of the application is checked and ensured.

OPC servers can provide communication from one OPC client to another, and if we consider this in reverse, OPC clients can communicate with more than one OPC server at the same time.





Purpose of OPC Protocol:

The purpose of the initial creation of OPC was to read data from automation devices such as PLC / DCS. However, OPC interface is also available in software to be found in other data communications today.

What is OPC DA?

OPC Data Access is a group of clients–server standards that provides specifications for communicating real-time data from data acquisition devices such as PLCs to display and interface devices like Human–Machine Interfaces (HMI), SCADA systems and also ERP/MES systems. The specifications focus on the continuous communication of data.

OPC DA Data consists of:
  1. a value,
  2. the quality of the value, and
  3. a timestamp.

OPC DA Architecture:





What is OPC UA?

OPC UA is the next generation of OPC technology. It’s a more secure, open, reliable mechanism for transferring information between servers and clients. It provides more open transports, better security and a more complete information model than the original OPC DA (a.k.a. OPC Classic). It provides a very flexible and adaptable mechanism for moving data between enterprise-type systems and the kinds of controls, monitoring devices and sensors that interact with real-world data.

OPC UA uses scalable platforms, multiple security models, multiple transport layers and a sophisticated information model to allow the smallest dedicated controller to freely interact with complex, high-end server applications. It can communicate anything from simple downtime status to massive amounts of highly complex plant-wide information.

OPC UA Architecture:

OPC UA Implementation Diagram





OPC UA Features:

  • Scalability – It’s scalable and platform-independent. Both high-end servers and low-end sensors can support it. UA uses discoverable profiles to include tiny, embedded platforms as servers in a UA system.
  • A Flexible Address Space – The address space is organized around the concept of an object. Objects are entities that consist of variables and methods and provide a standard way for servers to transfer information to clients.
  • Common Transports and Encodings – It uses standard transports and encodings to ensure that connectivity can be easily achieved in both embedded and enterprise environments.
  • Security – It implements a sophisticated security model that ensures the authentication of clients and servers, the authentication of users and the integrity of their communication.
  • Internet Capability – It’s fully capable of moving data over the internet.
  • A Robust Set of Services – OPC UA provides a full suite of services for eventing, alarming, reading, writing, discovery and more.
  • Certified Interoperability – It certifies profiles such that connectivity between a client and server using a defined profile can be guaranteed.
  • A Sophisticated Information Model – It profiles more than just an object model. True Information is shared between clients and servers because of the way it connects objects.
  • Sophisticated Alarming and Event Management – UA provides a highly configurable mechanism for providing alarms and event notifications to interested Clients. The alarming and event mechanisms go well beyond the standard change-in-value type alarming found in most protocols.
  • Integration with Standard Industry-Specific Data Models – The OPC Foundation is working with several industry trade groups that define specific information models for their industries to support those information models within UA.
OPC UA vs OPC DA protocol:




Let's Start Working on Open Source OPC UA Client using Python:

Installation:

 Using Python PIP:

pip install opcua

For Ubuntu / Raspberry Pi / Any Debian System Terminal:

apt install python-opcua # Library
apt install python-opcua-tools # Command-line tools

Dependencies:

  • Python > 3.4: cryptographydateutillxml and pytz.
  • Python 2.7 or pypy < 3: you also need to install enum34trollius (asyncio), and futures (concurrent.futures), with pip for example.

These features are implemented & successfully working:

  • connection to server, opening channel, session
  • browsing and reading attributes value
  • getting nodes by path and nodeids
  • creating subscriptions
  • subscribing to items for data change
  • subscribing to events
  • adding nodes
  • method call
  • user and password
  • history read
  • login with certificate
  • communication encryption
  • removing nodes
These features are not implemented yet:

  • localized text feature
  • XML protocol
  • UDP
  • maybe automatic reconnection...

Tested servers: freeopcua C++, freeopcua Python, prosys, kepware, beckhoff, winCC, B&R.


Code to Connect to OPC Server:

import sys
sys.path.insert(0, "..")
import logging
import time

try:
    from IPython import embed
except ImportError:
    import code

    def embed():
        vars = globals()
        vars.update(locals())
        shell = code.InteractiveConsole(vars)
        shell.interact()


from opcua import Client
from opcua import ua


class SubHandler(object):

    """
    Subscription Handler. To receive events from server for a subscription
    data_change and event methods are called directly from receiving thread.
    Do not do expensive, slow or network operation there. Create another 
    thread if you need to do such a thing
    """

    def datachange_notification(self, node, val, data):
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)


if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    #logger = logging.getLogger("KeepAlive")
    #logger.setLevel(logging.DEBUG)

    client = Client("opc.tcp://localhost:4840/freeopcua/server/")
    # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
    try:
        client.connect()
        client.load_type_definitions()  # load definition of server specific structures/extension objects

        # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
        root = client.get_root_node()
        print("Root node is: ", root)
        objects = client.get_objects_node()
        print("Objects node is: ", objects)

        # Node objects have methods to read and write node attributes as well as browse or populate address space
        print("Children of root are: ", root.get_children())

        # get a specific node knowing its node id
        #var = client.get_node(ua.NodeId(1002, 2))
        #var = client.get_node("ns=3;i=2002")
        #var = client.get_node("ns=2;g=1be5ba38-d004-46bd-aa3a-b5b87940c698")
        #print(var)
        #var.get_data_value() # get value of node as a DataValue object
        #var.get_value() # get value of node as a python builtin
        #var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type
        #var.set_value(3.9) # set node value using implicit data type

        # gettting our namespace idx
        uri = "http://examples.freeopcua.github.io"
        idx = client.get_namespace_index(uri)

        # Now getting a variable node using its browse path
        myvar = root.get_child(["0:Objects", "{}:MyObject".format(idx), "{}:MyVariable".format(idx)])
        obj = root.get_child(["0:Objects", "{}:MyObject".format(idx)])
        print("myvar is: ", myvar)

        # subscribing to a variable node
        handler = SubHandler()
        sub = client.create_subscription(500, handler)
        handle = sub.subscribe_data_change(myvar)
        time.sleep(0.1)

        # we can also subscribe to events from server
        sub.subscribe_events()
        # sub.unsubscribe(handle)
        # sub.delete()

        # calling a method on server
        res = obj.call_method("{}:multiply".format(idx), 3, "klk")
        print("method result is: ", res)

        embed()
    finally:
        client.disconnect()



Code to Connect to Kepware OPCUA Server:


import sys
sys.path.insert(0, "..")
import logging

from opcua import Client


class SubHandler(object):

    """
    Client to subscription. It will receive events from server
    """

    def datachange_notification(self, node, val, data):
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)


if __name__ == "__main__":
    #from IPython import embed
    logging.basicConfig(level=logging.WARN)
    client = Client("opc.tcp://192.168.56.100:49320/OPCUA/SimulationServer/")
    #client = Client("opc.tcp://192.168.56.100:4840/OPCUA/SimulationServer/")
    #client = Client("opc.tcp://olivier:olivierpass@localhost:53530/OPCUA/SimulationServer/")
    try:
        client.connect()
        root = client.get_root_node()
        print("Root is", root)
        print("childs of root are: ", root.get_children())
        print("name of root is", root.get_browse_name())
        objects = client.get_objects_node()
        print("childs og objects are: ", objects.get_children())


        tag1 = client.get_node("ns=2;s=Channel1.Device1.Tag1")
        print("tag1 is: {0} with value {1} ".format(tag1, tag1.get_value()))
        tag2 = client.get_node("ns=2;s=Channel1.Device1.Tag2")
        print("tag2 is: {0} with value {1} ".format(tag2, tag2.get_value()))

        handler = SubHandler()
        sub = client.create_subscription(500, handler)
        handle1 = sub.subscribe_data_change(tag1)
        handle2 = sub.subscribe_data_change(tag2)

        from IPython import embed
        embed()

        
        sub.unsubscribe(handle1)
        sub.unsubscribe(handle2)
        sub.delete()
    finally:
        client.disconnect()
For More Code Examples & reference, please refer to this link: python-opcua/examples at master · FreeOpcUa/python-opcua · GitHub

An {OpenSource} & Alternatives to {PAID} Apps

Standard
OSS & OSI


Let's First Understand "What is Open Source?"

As Per REDHAT:

Open source is a term that originally referred to open source software (OSS). Open source software is code that is designed to be publicly accessible—anyone can see, modify, and distribute the code as they see fit.

Open source software is developed in a decentralized and collaborative way, relying on peer review and community production. Open source software is often cheaper, more flexible, and has more longevity than its proprietary peers because it is developed by communities rather than a single author or company.




As Per OSI:


Open-source software is computer software that is released under a license in which the copyright holder grants users the rights to use, study, change, and distribute the software and its source code to anyone and for any purpose. Open-source software may be developed in a collaborative public manner.



Why Open Source is important?

Open source licensing encourages innovation through collaboration. Without it, many of the technologies we take for granted today would never have developed, or would be locked away behind patent law. The open source movement is the reason that technology has developed at such a breakneck pace for the past few decades.

Open source has become a movement and a way of working that reaches beyond software production. The open source movement uses the values and decentralized production model of open source software to find new ways to solve problems in their communities and industries.

Coming Soon, Continues....

Let’s Learn How to BrainFuck!

Standard

(Brainfuck Programming Language)

A brainfuck way to program the machine...

Brainfuck is probably the craziest language I have ever had the pleasure of coming across. Notable for its extreme minimalism, the language consists of only eight simple commands, a data pointer and an instruction pointer. While it is fully Turing complete, it is not intended for practical use, but to challenge and amuse programmers. Brainfuck simply requires one to break commands into microscopic steps.


The language's name is a reference to the slang term brainfuck, which refers to things so complicated or unusual that they exceed the limits of one's understanding. And, yes, there are quite a few tutorials that you will find on google about the language and how to program in it.

The language only consists of 8 operators, yet with the 8 operators,

<>+-[],.

You are capable of writing almost any program you can think of.

I would suggest an ASCII chart with all the ASCII chars and their decimal equivalent value. Next on the items would be a calculator. Any will do. It will help you figure out the Greatest Common Factors for use in incrementing a memory block quickly.

ASCII TABLE for Reference

Let's Start with the BASICS


Let’s First understand the meaning of 8 Operators:


> = increases memory pointer, or moves the pointer to the right 1 block.

< = decreases memory pointer, or moves the pointer to the left 1 block.

+ = increases value stored at the block pointed to by the memory pointer

- = decreases value stored at the block pointed to by the memory pointer

[ = like c while(cur_block_value != 0) loop.

] = if block currently pointed to's value is not zero, jump back to [

, = like c getchar(). input 1 character.

. = like c putchar(). print 1 character to the console


The C Equivalent of Above Operators are:

As the name suggests, Brainfuck programs tend to be difficult to comprehend. This is partly because any mildly complex task requires a long sequence of commands and partly because the program's text gives no direct indications of the program's state. These, as well as Brainfuck's inefficiency and its limited input/output capabilities, are some of the reasons it is not used for serious programming. Nonetheless, like any Turing complete language, Brainfuck is theoretically capable of computing any computable function or simulating any other computational model, if given access to an unlimited amount of memory.


Some Important Rules to consider:

  • Any arbitrary character besides the 8 listed above should be ignored by the compiler or interpreter. Characters besides the 8 operators should be con- sidered comments.

  • All memory blocks on the "array" are set to zero at the beginning of the program. And the memory pointer starts out on the very left most memory block.

  • Loops may be nested as many times as you want. But all [ must have a corre- sponding ].




Installation of BrainFuck Compiler:

 Follow this link to Download and Install Brainfuck Interpreter in Your System.

  1. BrainFuck 2.1.1 Free Download (soft112.com)

  2. Another link for Windows : Brainfuck Developer - a Brainfuck IDE (4mhz.de)



Writing First Program in BrainFuck:


Code 1:

[-]

Meaning: 

Well, that's what they say anyway, but I hardly consider that a program. All it does is enter a loop that decreases the value stored at the current memory pointer until it reaches zero, then exits the loop. But since all memory blocks start out at zero, it will never enter that loop.


Code 2:

+++++[-]


Above program in c:

*p=+5;

while(*p != 0){

*p--;

}


Meaning:

In the above program we are incrementing the current memory pointers value to 5, then entering a loop that decreases the value located at the memory pointer till it is zero, then exits the loop.


Code 3:

>>>>++

Above program in c:

*p=+5;

while(*p != 0){

*p--;

}


Meaning:

This will move the memory pointer to the fourth memory block, and increment the value stored there by 2. So it looks like



memory blocks

-------------

[0][0][0][2][0][0]...

^

memory pointer


As you can see in the 'k-rad' ASCII diagram, our memory pointer points to the fourth memory block, and it increments the value there by 1. since there was nothing there before, it now contains the value: 2. If we take that same program, and add more onto the end of it like:


>>>>++<<+>>+


At the end of our program, our memory layout will look like this:


memory blocks

-------------

[0][1][0][3][0][0]...

^

memory pointer


The pointer was moved to the fourth block, increased the value by 2, moved back 2 blocks to the second block, increment  value stored there by 1, and then the pointer moved 2 blocks to the right again to the fourth block and increment  value stored there by one. And at the end of the program the memory pointer lies back on the fourth memory block. That is fine and dandy, but we can't really see anything. So let's write a program that will produce actual output.


Let’s Say Hello World to BrainFuck!


[ This program prints "Hello World!" and a newline to the screen, its

  length is 106 active command characters. [It is not the shortest.]


  This loop is an "initial comment loop", a simple way of adding a comment

  to a BF program such that you don't have to worry about any command

  characters. Any ".", ",", "+", "-", "<" and ">" characters are simply

  ignored, the "[" and "]" characters just have to be balanced. This

  loop and the commands it contains are ignored because the current cell

  defaults to a value of 0; the 0 value causes this loop to be skipped.



]

++++++++               Set Cell #0 to 8

[

    >++++               Add 4 to Cell #1; this will always set Cell #1 to 4

    [                   as the cell will be cleared by the loop

        >++             Add 2 to Cell #2

        >+++            Add 3 to Cell #3

        >+++            Add 3 to Cell #4

        >+              Add 1 to Cell #5

        <<<<-           Decrement the loop counter in Cell #1

    ]                   Loop until Cell #1 is zero; number of iterations is 4

    >+                  Add 1 to Cell #2

    >+                  Add 1 to Cell #3

    >-                  Subtract 1 from Cell #4

    >>+                 Add 1 to Cell #6

    [<]                 Move back to the first zero cell you find; this will

                        be Cell #1 which was cleared by the previous loop

    <-                  Decrement the loop Counter in Cell #0

]                       Loop until Cell #0 is zero; number of iterations is 8


The result of this is:

Cell no :   0   1   2   3   4   5   6

Contents:   0   0  72 104  88  32   8

Pointer :   ^


>>.                     Cell #2 has value 72 which is 'H'

>---.                   Subtract 3 from Cell #3 to get 101 which is 'e'

+++++++..+++.           Likewise for 'llo' from Cell #3

>>.                     Cell #5 is 32 for the space

<-.                     Subtract 1 from Cell #4 for 87 to give a 'W'

<.                      Cell #3 was set to 'o' from the end of 'Hello'

+++.------.--------.    Cell #3 for 'rl' and 'd'

>>+.                    Add 1 to Cell #5 gives us an exclamation point

>++.                    And finally a newline from Cell #6

[ This program prints "Hello World!" and a newline to the screen, its

  length is 106 active command characters. [It is not the shortest.]


  This loop is an "initial comment loop", a simple way of adding a comment

  to a BF program such that you don't have to worry about any command

  characters. Any ".", ",", "+", "-", "<" and ">" characters are simply

  ignored, the "[" and "]" characters just have to be balanced. This

  loop and the commands it contains are ignored because the current cell

  defaults to a value of 0; the 0 value causes this loop to be skipped.

]

++++++++               Set Cell #0 to 8

[

    >++++               Add 4 to Cell #1; this will always set Cell #1 to 4

    [                   as the cell will be cleared by the loop

        >++             Add 2 to Cell #2

        >+++            Add 3 to Cell #3

        >+++            Add 3 to Cell #4

        >+              Add 1 to Cell #5

        <<<<-           Decrement the loop counter in Cell #1

    ]                   Loop until Cell #1 is zero; number of iterations is 4

    >+                  Add 1 to Cell #2

    >+                  Add 1 to Cell #3

    >-                  Subtract 1 from Cell #4

    >>+                 Add 1 to Cell #6

    [<]                 Move back to the first zero cell you find; this will

                        be Cell #1 which was cleared by the previous loop

    <-                  Decrement the loop Counter in Cell #0

]                       Loop until Cell #0 is zero; number of iterations is 8


The result of this is:

Cell no :   0   1   2   3   4   5   6

Contents:   0   0  72 104  88  32   8

Pointer :   ^


>>.                     Cell #2 has value 72 which is 'H'

>---.                   Subtract 3 from Cell #3 to get 101 which is 'e'

+++++++..+++.           Likewise for 'llo' from Cell #3

>>.                     Cell #5 is 32 for the space

<-.                     Subtract 1 from Cell #4 for 87 to give a 'W'

<.                      Cell #3 was set to 'o' from the end of 'Hello'

+++.------.--------.    Cell #3 for 'rl' and 'd'

>>+.                    Add 1 to Cell #5 gives us an exclamation point

>++.                    And finally a newline from Cell #6


The above in ONE Line:

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.


Output:

Hello World!


Let’s code to find Factorial of a Number in Brainfuck:

+++++++++++++++++++++++++++++++++ c1v33 : ASCII code of !

>++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++ c2v61 : ASCII code of =

>++++++++++ c3v10 : ASCII code of EOL

>+++++++ c4v7  : quantity of numbers to be calculated

> c5v0  : current number (one digit)

>+ c6v1  : current value of factorial (up to three digits)

<< c4    : loop counter

[ block : loop to print one line and calculate next

>++++++++++++++++++++++++++++++++++++++++++++++++. c5    : print current number

------------------------------------------------ c5    : back from ASCII to number

<<<<.-.>.<.+ c1    : print !_=_


>>>>> block : print c6 (preserve it)

> c7v0  : service zero

>++++++++++ c8v10 : divizor

<< c6    : back to dividend

[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<] c6v0  : divmod algo borrowed from esolangs; results in 0 n d_n%d n%d n/d

>[<+>-] c6    : move dividend back to c6 and clear c7

>[-] c8v0  : clear c8


>> block : c10 can have two digits; divide it by ten again

>++++++++++ c11v10: divizor

< c10   : back to dividend

[->-[>+>>]>[+[-<+>]>+>>]<<<<<] c10v0 : another divmod algo borrowed from esolangs; results in 0 d_n%d n%d n/d

>[-] c11v0 : clear c11

>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]c13v0 : print nonzero n/d (first digit) and clear c13

<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] c12v0 : print nonzero n%d (second digit) and clear c12

<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-] c9v0  : print any n%d (last digit) and clear c9


<<<<<<. c3    : EOL

>>+ c5    : increment current number

block : multiply c6 by c5 (don't preserve c6)

>[>>+<<-] c6v0  : move c6 to c8

>> c8v0  : repeat c8 times

[

<<<[>+>+<<-] c5v0  : move c5 to c6 and c7

>>[<<+>>-] c7v0  : move c7 back to c5

>-

]

<<<<- c4    : decrement loop counter

]




Fibonacci Series  Program in BrainFuck!:

+++++++++++ number of digits to output

> #1

+ initial number

>>>> #5

++++++++++++++++++++++++++++++++++++++++++++ (comma)

> #6

++++++++++++++++++++++++++++++++ (space)

<<<<<< #0

[

  > #1

  copy #1 to #7

  [>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]


  <

  divide #7 by 10 (begins in #7)

  [

    >

    ++++++++++  set the divisor #8

    [

      subtract from the dividend and divisor

      -<-

      if dividend reaches zero break out

        copy dividend to #9

        [>>+>+<<<-]>>>[<<<+>>>-]

        set #10

        +

        if #9 clear #10

        <[>[-]<[-]]

        if #10 move remaining divisor to #11

        >[<<[>>>+<<<-]>>[-]]

      jump back to #8 (divisor possition)

      <<

    ]

    if #11 is empty (no remainder) increment the quotient #12

    >>> #11

    copy to #13

    [>>+>+<<<-]>>>[<<<+>>>-]

    set #14

    +

    if #13 clear #14

    <[>[-]<[-]]

    if #14 increment quotient

    >[<<+>>[-]]

    <<<<<<< #7

  ]


  quotient is in #12 and remainder is in #11

  >>>>> #12

  if #12 output value plus offset to ascii 0

  [++++++++++++++++++++++++++++++++++++++++++++++++.[-]]

  subtract #11 from 10

  ++++++++++  #12 is now 10

  < #11

  [->-<]

  > #12

  output #12 even if it's zero

  ++++++++++++++++++++++++++++++++++++++++++++++++.[-]

  <<<<<<<<<<< #1


  check for final number

  copy #0 to #3

  <[>>>+>+<<<<-]>>>>[<<<<+>>>>-]

  <- #3

  if #3 output (comma) and (space)

  [>>.>.<<<[-]]

  << #1


  [>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-

]



And the no comment neat little block:

+++++++++++

>+>>>>++++++++++++++++++++++++++++++++++++++++++++

>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>

+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-

<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<

-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]

>[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++

+++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++

++++++++++++++++++++++++++++++++++++++++++++.[-]<<

<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<

[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]



Link for Brainfuck - C Interpreter:

BrainFuck (github.com)  To C


Link for Brainfuck - Java Interpreter:

BrainFuck Interpreter in Java - GeeksforGeeks


Would you still like to FUCK your Brain!
😈😜😀

Reference Links:

BrainFuck Interpreter in Java - GeeksforGeeks

brainfuck - Esolang (esolangs.org)

http://esoteric.sange.fi/

 programs in Brainfuck? - Stack Overflow

Wikipedia

Google

Brainfuck: code that was designed to hurt | The Outline

Coding Games and Programming Challenges to Code Better (codingame.com)