7.10. Linking a simulation program to the BCVTB

This section describes an example that illustrates how to link a simulation program to the BCVTB in such a way that they exchange data at a fixed time step through a BSD socket connection. We will consider a system with two rooms. Each room has a heater that is controlled by a proportional controller. We will implement the simulation program for the two rooms in a C program, and we will link it to a controller that is implemented in Ptolemy II.

Let k{1, 2, ...} denote equally spaced time steps and let i{1, 2} denote the number of the room. For the k -th time step and the room number i , let T i ( k ) denote the room temperature and let u i ( k ) denote the control signal for the heater. The room temperature is governed by

T i ( k + 1 ) = T i ( k ) + Δ t C i ( U A ) i ( T o u t - T i ( k ) ) + Δ t C i Q 0 i u i ( k ) ,

with initial conditions T i ( 0 ) = T 0 i , where Δ t is the time interval, Ci is the room thermal capacity, (UA)i is the room heat loss coefficient, Tout is the outside temperature, Q0i is the nominal capacity of the heater and T0i is the initial temperature. In these equations, we assumed that the communication time step is small enough to be used as the integration time step. If this is not the case, we could use a different integration time step and synchronize the integration time step with the communication time step.

The governing equation for the control signal is u i ( k + 1 ) = min ( 1 , max ( 0 , γ i ( T s e t i - T i ( k ) ) ) ) , where γi > 0 is the proportional gain, Tseti is the set point temperature and the min ( , ) and max ( , ) functions limit the control signal between 0 and 1 .

Figure 7.1 shows a source code snippet of the implemented client. This source code can be found in the directory bcvtb/examples/c-room. A similar implementation in Fortran 90 can be found in the directory bcvtb/examples/f90-room.

Figure 7.1. Source code for a model of two rooms that is implemented in the C language.

  1 // Establish the client socket
      const int sockfd = establishclientsocket("socket.cfg");
      if (sockfd < 0){
        fprintf(stderr,"Error: Failed to obtain socket file descriptor.\n");
  5     exit((sockfd)+100);  }
      // Simulation loop
      while(1){
        // assign values to be exchanged
        for(i=0; i < nDblWri; i++)  dblValWri[i]=TRoo[i];
 10     // Exchange values
         retVal = exchangedoubleswithsocket(&sockfd, &flaWri, &flaRea,
    				       &nDblWri, &nDblRea,
    				       &simTimWri, dblValWri, 
    				       &simTimRea, dblValRea);
 15     /////////////////////////////////////////////////////////////
        // Check flags
        if (retVal < 0){
          sendclientmessage(&sockfd, &cliErrFla);
          printf("Simulator received value %d from socket.\n", retVal);
 20       closeipc(&sockfd);    exit((retVal)+100);  }
        if (flaRea == 1){
          printf("Simulator received end of simulation signal.\n");
          closeipc(&sockfd);    exit(0);  }
        if (flaRea != 0){
 25       printf("Simulator received flag = %d. Exit simulation.\n", flaRea);
          closeipc(&sockfd);    exit(1);  }
        /////////////////////////////////////////////////////////////
        // No flags found that require the simulation to terminate. 
        // Assign exchanged variables
 30     for(i=0; i < nRoo; i++)
          u[i] = dblValRea[i];
        /////////////////////////////////////////////////////////////
        // Having obtained u_k, we compute the new state x_k+1 = f(u_k).
        // This is the actual simulation time step of the client
 35     for(i=0; i < nRoo; i++)
          TRoo[i] = TRoo[i] + delTim/C[i] * ( UA * (TOut-TRoo[i] ) 
                    + Q0Hea * u[i] );
        simTimWri += delTim; // advance simulation time
      } // end of simulation loop


There are three functions that interface the client with the BCVTB: The function call establishclientsocket establishes the socket connection from the client to the middleware. The return value is an integer that references the socket. This descriptor is then used on line 11 as an argument to the function call exchangedoubleswithsocket. This function writes data to the socket and reads data from the socket. Its arguments are the socket file descriptor, a flag to send a signal to the middleware (a non-zero value means that the client will stop its simulation) and a flag received from the middleware (a non-zero value indicates that no further values will be written to or read from the socket by the client). The remaining arguments are the array lengths and the array data to be written to and read from the middleware. After the call to exchangedoubleswithsocket follows error handling. The test retVal < 0 checks for errors during the communication. If there was an error, then a message is sent to the server to indicate that the client will terminate the co-simulation. Finally, the socket connection is closed by calling closeipc.

To compile the source code, type on a command shell

cd bcvtb/examples/c-room
ant all

This will invoke the ant build system, which calls the file bcvtb/examples/c-room/build.xml that contains the compiler and linker commands.

To simulate this example, we implemented the controller directly in the middleware, using actors from the Ptolemy II library. However, the controller could as well be implemented in Modelica, MATLAB, Simulink or in a user written program that communicates through a BSD socket similarly to the C client above. Figure 7.2 shows the system diagram with the actor for the controller and the actor that interfaces the simulation program.

Figure 7.2.  Ptolemy II system model that connects a model of a controller and a room.

Ptolemy II system model that connects a model of a controller and a room.