Connecting to the simulation - LorinczMate1990/FluidControlSim GitHub Wiki

After you created your model in the IDE, you can connect to it via TCP. (Currently, it's only possible from the localhost at the port 8883, but it's very easy to modify the connection parameters in the fluidsim_client.py file. You have to modify the server = fluidsim_server.Server("localhost", 8883, simulator) line.)

Using the NetworkSimulatorFacade class

If you write your control application in Python, you can use the NetworkSimulatorFacade class, which has everything to connect to or manipulate your model. If you use other platforms, you have to implement the JSON messages.

The NetworkSimulatorFacade has a constructor and 17 public methods. The constructor needs a socket which is connected to the running client. The fallowing code block shows te initialization of a NetworkSimulatorFacade.

   IP = "localhost"
   port = 8883
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.connect((IP, port))

   simulator = NetworkSimulatorFacade(s)

After you initialized your simulator instance, you can call its methods to reach and manipulate the simulation.

Methods of the simulator

def addContainer(self, maxWaterLevel, area, static)

This function creates a container with the given maxWaterLevel (real) and area (real). Static can be True or False. If it's True, the fluid level in the container won't change during simulation. (It's useful for source or sink containers.)

The function returns with the ID of the new container. (ID is an integer, every object in the simulation has a unique ID.)

def addPipe(self, ID1, ID2, radius, length, height)

This function creates a pipe to connect two containers given by their IDs. (ID1 and ID2). The fallowing image explains the radius (float), length (float) and height (float):

The function returns with the ID of the new pipe. (ID is an integer, every object in the simulation has a unique ID.)

def addValve(self, ID1, ID2, minRadius, maxRadius, length, height)

This function is similar to addPipe, but it creates a valve between the two referred containers. To understand how pipes work in FluidControlSim, please check the Using the IDE wikipage.

The function returns with the ID of the new valve. (ID is an integer, every object in the simulation has a unique ID.)

def addPump(self, ID1, ID2, radius, length, height, maxPressure)

This function is similar to addPipe, but it creates a pump between the two referred containers. To understand how pumps work in FluidControlSim, please check the Using the IDE wikipage.

The function returns with the ID of the new pump. (ID is an integer, every object in the simulation has a unique ID.)

def setValveState(self, actuatorId, percent)

This function sets the setpoint of the given valve. (The valve must be given by its ID.) This only can change the setpoint, the real state will change according to its differential equitation.

def openValve(self, actuatorId, percentPoint) and def closeValve(self, actuatorId, percentPoint)

These functions are similar to setValveState, but they will increase or decrease the setpoint of the valve by percentPoint.

setPumpPerformance(self, actuatorId, percent), incPumpPerformance(self, actuatorId, percent) and decPumpPerformance(self, actuatorId, percent)

Like setValveState, these functions can set, increase or decease the setpoint of a pump.

getFluidsimObjectDescription(self, objectID)

The simulation objects are the descendants of FluidsimObject (fluidsim_core.py) and they have a getDescription method which has to return by a dict. This dict can contain any relevant parameter of the object. The getFluidsimObjectDescription method gives back this object. (For more details, you can check the fluidsim_core.py.)

setContainerState(self, containerID, fluidTemperature, fluidLevel)

This method can set the temperature and the fluid level of a container given by its ID. (It can modify static containers, too.)

run(self, deltaT, repeat=1)

This method can evaluate the simulation scene. For every active elements (pumps, pipes and valves) the simulation calculates the flow speed according to the current pressure conditions with the fallowing equitation:

Where r is the radius of the pipe (valve can modify this value), p_0 and p_1 are the pressures in the two endpoint of the pipe (pumps can modify these values), Eta is the viscosity and l is the length of the pipe.

With the flow speed, it will calculate the fluid quantity which can flow through the pipe with the calculated speed during dT seconds:

If you increase deltaT, the simulation will be faster but also inaccurate because it won't fallow the change of the pressure conditions. The simulation will do the deltaT steps repeat times.

getListOfIds(self)

This function returns by a list containing every ID in the simulation.

getContainersOfActiveElement(self, activeElementID)

This function is useful when you want to discover the topology of the simulation. The input parameter is an ID of an active element (pipe, valve or pump) and it returns by a two-element tuple containing two container IDs. (Every active elements need two containers which are connected by the pump.)

deleteFluidsimObject(self, objectID)

This function deletes the object given by its objectID from the simulation. (If you remove a container, every active elements connected to it will be removed, too.)

Using the DirectSyncronizedSimulatorFacade class

If you need faster simulation and don't need the graphical client, you can run the simulator without it. For this, you have to create an instance from DirectSyncronizedSimulatorFacade.

    simulator = DirectSyncronizedSimulatorFacade()

With this object, you can call all methods detailed in the previous section but your calls won't generate TCP packets, they will manipulate the scene inside your freshly created object. This can speed up the simulation, but you have to initialize your simulation scene inside your application.

The DirectSyncronizedSimulatorFacade and NetworkSimulatorFacade are the descendant of the same class (AbstractSimulatorFacade), you can easily test your application with the graphical client via NetworkSimulatorFacade and you can switch it by DirectSyncronizedSimulatorFacade later.

Using other environments

To connect to the simulator, you need to send and receive TCP stream. This wiki page won't describe the packets, but you can easily reproduce them by checking the Commands.py and SocketHandler.py.

Commands.py contains a class for every packet, and every class has a buildDict static function which generates a dict.

SocketHandler.py contains the SocketHandler class which converts these dicts into string (__dictToPacketString(self, d)) and parses the stream into dicts (__packetStringToDict). As you can see, the packets are plain JSON strings started with '#' and ended with '!'.

In the future, I will port the API into different platforms. My current targets are Rockwell RSLogix PLC-s.