コード例 #1
0
ファイル: serve.c プロジェクト: CardonaPinball/freewpc
static void set_ball_count_task (void) {
	device_t *dev = device_entry (DEVNO_TROUGH);
	U8 max_live_balls;
	U8 retries;
	U8 	temp_live_balls_wanted;

	max_live_balls = live_balls;
	temp_live_balls_wanted = live_balls_wanted - live_balls;
	retries = 2;

	//first pass - quick fire balls
	MB_SERVING = TRUE;
	while (temp_live_balls_wanted) {
		temp_live_balls_wanted--;
		sol_request_async (SOL_BALLSERVE);
		task_sleep (TIME_500MS); //worked good at 700 - just giving a little margin here
		task_sleep (TIME_400MS);
		sol_request_async (MACHINE_LAUNCH_SOLENOID);
		task_sleep (TIME_500MS);//worked good at 300 - just giving a little margin here
	}//end of rapid fire serving
	MB_SERVING = FALSE;

	//check trough to see if live balls is accurate
			task_sleep (TIME_1S);
			live_balls = (5 - device_recount(device_entry (DEVNO_TROUGH)) );
			max_live_balls = live_balls;

	//if still not all balls out there, retry slower - 2 times
	// we will usually arrive at this place on a ball-saving type multiball
	//where balls are constantly draining and refiring
	//this is okay, since the refiring will slow down which is sort of a punishment to
	//the player for allowing the balls to drain so fast
	while (max_live_balls < live_balls_wanted && retries) {
		retries--;

		/* Are there enough balls in the trough to satisfy another kick request?
		 * If not, then we need to add the balls from somewhere else.*/
		if (dev->actual_count < dev->kicks_needed)	callset_invoke (trough_rescue);
		else										serve_ball_auto ();

		task_sleep (SET_BALL_COUNT_TASK_DELAY);

		/* As long as there is a ball on the shooter, wait before trying
		to continue.  This flag will clear once the shooter switch
		has cleared for a few seconds. */
		while (global_flag_test (GLOBAL_FLAG_BALL_AT_PLUNGER))
			task_sleep (TIME_133MS);

		/* See if the ball count went up, indicating success */
		if (live_balls > max_live_balls)
			max_live_balls = live_balls;
	}//end of retry section

	task_exit ();
} //end of function
コード例 #2
0
ファイル: search.c プロジェクト: Dave2084/freewpc
/** Returns true if the given solenoid is OK to fire during
 * a ball search.  The following should be avoided:
 * - kickout coils from ball devices
 * - the knocker
 * - flashers
 * - anything else the machine description says not to fire
 */
static bool ball_search_solenoid_ok (U8 sol)
{
	device_t *dev;

#if !defined(MACHINE_SOL_FLASHERP)
	/* If the machine description is not proper, then we can't know
	 * for sure which solenoids are OK, so don't fire _any_.  */
	return (FALSE);
#endif

#if defined(MACHINE_SOLENOID_P)
	/* If it's not a valid solenoid number, don't allow it.  This
	skips coils that are not installed. */
	if (!MACHINE_SOLENOID_P (sol))
		return FALSE;
#endif

	/* Skip known coils which should always be skipped. */
	if (MACHINE_SOL_FLASHERP(sol)
#ifdef MACHINE_BALL_SERVE_SOLENOID
		 || (sol == MACHINE_BALL_SERVE_SOLENOID)
#endif
#ifdef MACHINE_KNOCKER_SOLENOID
		 || (sol == MACHINE_KNOCKER_SOLENOID)
#endif
#ifdef MACHINE_SOL_NOSEARCHP
		 || (MACHINE_SOL_NOSEARCHP(sol))
#endif
		 )
		return (FALSE);

	/* Also check for all ball device kick coils; skip them */
	for (dev=device_entry(0); dev < device_entry(NUM_DEVICES); dev++)
	{
		if (sol == dev->props->sol)
		{
			/* This coil controls a ball device. */

			/* If there are no balls detected here, pulse it */
			if (dev->actual_count == 0)
				return TRUE;

			/* If chase ball is turned off, then during the 5th ball search,
			pulse it */
			if (!chase_ball_enabled () && ball_search_count == 5)
				return (TRUE);

			/* Default is NOT to fire such a coil */
			return (FALSE);
		}
	}

	/* OK, you can use it. */
	return (TRUE);
}
コード例 #3
0
ファイル: tz_amode.c プロジェクト: SonnyJim/freewpc
static void lock_and_outhole_monitor (void)
{
	/* Wait for balls to settle/amode to start before emptying
	 * locks/outhole */
	task_sleep_sec (3);
	while (!in_live_game)
	{
		if (switch_poll (SW_LOCK_LOWER))
		{
			device_request_kick (device_entry (DEVNO_LOCK));
		}

		if (switch_poll (SW_OUTHOLE))
		{
			sol_request (SOL_OUTHOLE);
		}
		if (!switch_poll (SW_AUTOFIRE2))
		{
			callset_invoke (clear_autofire);	
		}
		/* Wait for the balls to be cleared before starting again */
		task_sleep_sec (3);
	}
	task_exit ();
}
コード例 #4
0
ファイル: stress.c プロジェクト: Curbfeeler/freewpc
/**
 * Simulate a ball drain condition.
 * This is simulated occasionally during multiball play, and can be
 * forced by pressing the Start Button.  The manual approach is the
 * only way to end a ball, otherwise the simulation goes on forever.
 */
void switch_stress_drain (void)
{
#ifdef DEVNO_TROUGH
	device_t *dev = device_entry (DEVNO_TROUGH);
	device_call_op (dev, enter);
#endif
}
コード例 #5
0
ファイル: serve.c プロジェクト: CardonaPinball/freewpc
/**
 * Serve a new ball to the manual shooter lane.
 * This function is the preferred method to serve a ball to a manual
 * plunger at the beginning of a ball and after a ball lock.
 * It is not used for autoplunges.
 */
void serve_ball (void) {
#ifdef DEVNO_TROUGH
	valid_playfield = FALSE;
	callset_invoke (serve_ball);
	effect_update_request ();
	device_request_kick (device_entry (DEVNO_TROUGH));
#endif /* DEVNO_TROUGH */
} //end of function
コード例 #6
0
ファイル: powerball_test.c プロジェクト: SonnyJim/freewpc
void pb_test_enter (void)
{
	sound_send (SND_TEST_ENTER);
	switch (pb_test_command)
	{
		case KICK_TROUGH:
			device_request_kick (device_entry (DEVNO_TROUGH));
			break;
		case KICK_LOCK:
			device_request_kick (device_entry (DEVNO_LOCK));
			break;
		case RELEASE_GUMBALL:
			gumball_release ();
			break;
		case LOAD_GUMBALL:
			gumball_load_from_trough ();
			break;
	}
}
コード例 #7
0
ファイル: device.c プロジェクト: Dmilo/freewpc
/** Called by the trough when a ball has entered it.
 * The intent is to signal that there is one less live ball than before.
 */
void device_remove_live (void)
{
	/* If any balls were missing, and now one is rediscovered in the
	 * trough, then just hold onto it.  This condition was seen when
	 * the game thought 1 ball was in play, but 2 were on the playfield. */
	if (missing_balls > live_balls)
	{
		callset_invoke (missing_ball_found);
		missing_balls--;
	}
	else if (live_balls > 0)
	{
		/* Decrement the count of balls in play.  Now what? */
		live_balls--;
		if (in_game && !in_bonus)
		{
			/* Notify that the ball count changed */
			callset_invoke (ball_count_change);

			/* See if this qualifies as a ball drain.  Any event receiver
			can return FALSE here if it is not to be treated as a drain;
			e.g., when a ballsaver is active.  In these cases, the
			event function is also responsible for putting the ball
			back into play. */
			if (!callset_invoke_boolean (ball_drain))
				return;

			/* OK, at this point, it is a true ball drain event.
			See how many balls are in play now. */
			switch (live_balls
#ifdef DEVNO_TROUGH
				 + device_entry (DEVNO_TROUGH)->kicks_needed
#endif
				)
			{
				case 0:
					/* With zero balls in play, this is end of ball.
					This function usually does not return; it will stop just about
					every task running to reset for the next ball. */
					end_ball ();
					return;

				case 1:
					/* Multiball modes like to know when single ball play resumes. */
					callset_invoke (single_ball_play);
					break;
				default:
					break;
			}
		}
	}
}
コード例 #8
0
ファイル: serve.c プロジェクト: CardonaPinball/freewpc
/**
 * Autolaunch a new ball into play from the trough.  This is the
 * preferred API to use by ballsavers.
 */
void serve_ball_auto (void) {
#ifdef DEVNO_TROUGH
	/* Fall back to manual ball serve if there is no autoplunger. */
	if (!have_auto_serve_p ())  serve_ball ();
	else {
		set_valid_playfield ();
		/* TZ's autoplunger is a little different, so it is handled specially. */
#if defined(MACHINE_TZ)
		autofire_add_ball ();
#else
		device_request_kick (device_entry (DEVNO_TROUGH));
#endif
	} //end of else
#endif /* DEVNO_TROUGH */
} //end of function
コード例 #9
0
ファイル: device.c プロジェクト: Dmilo/freewpc
/** Update the global state of the machine.  This happens nearly
 * anytime a change happens locally within a particular device. */
void device_update_globals (void)
{
	devicenum_t devno;
	U8 held_balls_now = 0;

	/* TODO - mostly this is called from the context of a single device.
	Could simplify all the recounting of other devices... */

	/* Recount the total number of balls that are held,
	excluding those that are locked and those in the trough. */
	counted_balls = 0;
	held_balls_now = 0;
	for (devno = 0; devno < NUM_DEVICES; devno++)
	{
		device_t *dev = device_entry (devno);
		counted_balls += dev->actual_count;

		if (!trough_dev_p (dev))
			if (dev->actual_count > dev->max_count)
				held_balls_now += dev->actual_count - dev->max_count;
	}

	/* Update held_balls atomically */
	held_balls = held_balls_now;

	/* Update count of how many balls are missing */
	missing_balls = max_balls - counted_balls;

	/* If 'missing' went negative, this means there are more
	balls detected than expected. */
	if (missing_balls > 0xF0)
	{
		missing_balls = 0;
	}

	dbprintf ("Counted %d Missing %d Live %d Heldup %d\n",
		counted_balls, missing_balls, live_balls, held_balls);

	/* If any balls are held up temporarily (more than "max" are
	 * in the device presently), then delay timers */
	if (in_live_game)
	{
		if (held_balls > 0)
			timed_game_suspend ();
		else
			timed_game_resume ();
	}
}
コード例 #10
0
ファイル: rules.c プロジェクト: Dmilo/freewpc
CALLSET_ENTRY (ball_lock, trunk_back_shot)
{
	if (ball_lock_can_be_collected () && balls_locked < 2)
	{
		device_t *dev = device_entry (DEVNO_SUBWAY);
		if (device_recount (dev) < 2)
		{
			device_lock_ball (dev);
		}
		else
		{
			/* start kickout warning effect */
		}
		ball_lock_award ();
	}
}
コード例 #11
0
ファイル: serve.c プロジェクト: Elwoodbe/freewpc
static void set_ball_count_task (void)
{
    device_t *dev = device_entry (DEVNO_TROUGH);
    U8 max_live_balls;
    U8 retries;

    /* While we are launching balls, we monitor 'live_balls' to
    check that it is going up. */
    max_live_balls = live_balls;

    /* Set the number of times we will attempt to kick a ball.
    This is the number of balls that need to be added to play,
    plus 2 to handle errors.  After this, we give up. */
    retries = live_balls_wanted - max_live_balls + 2;

    while (retries && max_live_balls < live_balls_wanted)
    {
        retries--;

        /* Are there enough balls in the trough to satisfy another
        kick request?  If not, then we need to add the balls from
        somewhere else.  This is machine-specific. */
        if (dev->actual_count < dev->kicks_needed)
        {
            callset_invoke (trough_rescue);
        }
        else
        {
            serve_ball_auto ();
        }

        /* Wait a bit for the ball to make it to the shooter lane. */
        task_sleep (TIME_2S + TIME_500MS);

        /* As long as there is a ball on the shooter, wait before trying
        to continue.  This flag will clear once the shooter switch
        has cleared for a few seconds. */
        while (global_flag_test (GLOBAL_FLAG_BALL_AT_PLUNGER))
            task_sleep (TIME_133MS);

        /* See if the ball count went up, indicating success */
        if (live_balls > max_live_balls)
            max_live_balls = live_balls;
    }
    task_exit ();
}
コード例 #12
0
ファイル: gumball.c プロジェクト: SonnyJim/freewpc
/* Called whenever the far left trough switch is tripped.
The sole purpose of this to determine when there are too 
many balls in the trough, and one needs to be fired into the
gumball out of the way.  If a ball remains on this switch for
3 seconds, then it is assumed there are 4 balls in the trough
and one must be loaded. */
void sw_far_left_trough_monitor (void)
{
	U8 timeout = TIME_3S / TIME_200MS;
	device_t *dev = device_entry (DEVNO_TROUGH);

 	while (task_find_gid (GID_DEVICE_PROBE))
		task_sleep_sec (1);

	dbprintf ("Far left trough check\n");
	/* Poll the switch for up to 3 seconds.  If it ever opens,
	then abort.  It must stay closed and the trough must
	remain full in order for us to continue. */
	while (timeout > 0)
	{
		task_sleep (TIME_200MS);
		if ((!switch_poll_logical (SW_FAR_LEFT_TROUGH))
			|| (dev->actual_count != dev->size))
		{
			dbprintf ("Far left trough not stable\n");
			task_exit ();
		}
		timeout--;
	}

	/* If a ball is known to be in play, then delay the
	load */
	while (valid_playfield)
	{
		dbprintf ("Far left trough : waiting to load\n");
		task_sleep_sec (1);
	}

	/* Start the load */
	dbprintf ("Far left trough stable : loading gumball\n");
	if (hold_balls_in_autofire)
	{
		autofire_add_ball ();
	}
	else
		gumball_load_from_trough ();
	task_exit ();
}
コード例 #13
0
ファイル: device.c プロジェクト: Dmilo/freewpc
/** Initialize the device subsystem */
void device_init (void)
{
	device_t *dev;
	U8 i;

	max_balls = MACHINE_MAX_BALLS;
	counted_balls = MACHINE_TROUGH_SIZE;
	missing_balls = 0;
	live_balls = 0;
	kickout_unlock_all ();
	held_balls = 0;
	device_game_start_errors = 0;

	for (i=0; i < NUM_DEVICES; i++)
	{
		dev = device_entry (i);
		device_clear (dev);
		device_register (i, &device_properties_table[i]);
	}
}
コード例 #14
0
ファイル: serve.c プロジェクト: Dave2084/freewpc
static void set_ball_count_task (void)
{
	device_t *dev = device_entry (DEVNO_TROUGH);
	U8 max_live_balls;
	U8 retries;

	/* While we are launching balls, we monitor 'live_balls' to
	check that it is going up. */
	max_live_balls = live_balls;

	/* Set the number of times we will attempt to kick a ball.
	This is the number of balls that need to be added to play,
	plus 2 to handle errors.  After this, we give up. */
	retries = live_balls_wanted - max_live_balls + 2;

	while (retries && max_live_balls < live_balls_wanted)
	{
		retries--;

		/* Are there enough balls in the trough to satisfy another
		kick request?  If not, then we need to add the balls from
		somewhere else.  This is machine-specific. */
		if (dev->actual_count < dev->kicks_needed)
		{
			callset_invoke (trough_rescue);
		}
		else
		{
			serve_ball_auto ();
		}

		/* Delay slightly, and see if the live count changed.
		We only track its maximum */
		task_sleep (TIME_1500MS);

		/* See if the ball count went up, indicating success */
		if (live_balls > max_live_balls)
			max_live_balls = live_balls;
	}
	task_exit ();
}
コード例 #15
0
ファイル: device.c プロジェクト: Dmilo/freewpc
/** Requests that a device allow a ball lock. */
void device_lock_ball (device_t *dev)
{
#ifdef DEVNO_TROUGH
	device_t *trough = device_entry (DEVNO_TROUGH);
#endif

	/* If the device is already locking as many balls
	as it can hold, then trying to lock another ball here
	is an error. */
	if (dev->max_count >= dev->size)
		fatal (ERR_LOCK_FULL_DEVICE);

	dbprintf ("Lock ball in devno %d\n", dev->devno);

	/* Update count of balls that will be held here. */
	device_enable_lock (dev);

	/* Say that there is one less active ball on the
	playfield now, assuming a ball is still physically present */
	if (device_kickable_count (dev))
		live_balls--;

	/* If the trough is not empty, we can serve another ball from the
	trough to continue play.  Otherwise, it will have to come from
	somewhere else.  The default is to serve it from the same device,
	but this can be overriden by the 'empty_trough_kick' event. */
#ifdef DEVNO_TROUGH
	if (trough->actual_count > 0)
	{
		serve_ball ();
	}
	else
#endif
	{
		if (!callset_invoke_boolean (empty_trough_kick))
			device_unlock_ball (dev);
	}
}
コード例 #16
0
ファイル: autofire.c プロジェクト: SonnyJim/freewpc
/** Request that a new ball be autolaunched into play. */
void autofire_add_ball (void)
{
    autofire_request_count++;

    if (!in_game || switch_poll_logical (SW_FAR_LEFT_TROUGH))
    {
        /* For special situations.  If not in game, the
        kick_attempt hook won't be called to open for trough.
        If far left trough is set, the kick will appear failed
        because the trough count won't change (even though a
        ball was successfully kicked).  In these cases, do it
        manually.  However, you get no retry capability here. */
        autofire_open_for_trough ();
        /* Wait for divertor to open */
        task_sleep_sec (2);
        sol_request (SOL_BALL_SERVE);
    }
    else
    {
        /* The normal way to kick a ball from the trough.
         * dev_trough_kick_attempt will be called next */
        device_request_kick (device_entry (DEVNO_TROUGH));
    }
}
コード例 #17
0
ファイル: stress.c プロジェクト: Curbfeeler/freewpc
/**
 * This task runs during a switch stress test.  During a game,
 * it randomly invokes switch handlers as if the switches had actually
 * been activated by the pinball.
 */
void switch_stress_task (void)
{
	U8 sw;
	const switch_info_t *swinfo;
	task_pid_t tp;

	/* Delay a few seconds before starting the simulation.  This allows
	time for the Start Button to be used to add players, instead of simulating
	endball. */
	task_sleep_sec (3);

	task_create_peer (switch_stress_flipper_task);
	for (;;)
	{
		task_sleep (TIME_100MS);
		if (in_test)
			continue;

		/* Choose a switch at random.  Skip certain well-known switches that are
		 * never to be activated. */
		sw = random_scaled (NUM_SWITCHES);
#ifdef SW_ALWAYS_CLOSED
		if (sw == SW_ALWAYS_CLOSED)
			continue;
#endif
#ifdef MACHINE_OUTHOLE_SWITCH
		if (sw == MACHINE_OUTHOLE_SWITCH)
			continue;
#endif

		/* Lookup the switch properties.  Skip switches which aren't normally
		 * activated on the playfield.  For switches in a ball container,
		 * simulate device entry there, otherwise simulate a switch event. */
		swinfo = switch_lookup (sw);

		if (SW_HAS_DEVICE (swinfo))
		{
			device_t *dev = device_entry (SW_GET_DEVICE (swinfo));
			if (trough_dev_p (dev))
			{
				/* Don't always trigger the trough device.  The probability
				of a ball drain is treated as proportional to the number of
				balls in play.  So in big multiballs, we allow this to happen
				more frequently.
					We need to do this occasionally so that multiball modes
				will eventually end, else nothing else gets tested. */
				if (random () < CONFIG_STRESS_DRAIN_PROB * live_balls)
					switch_stress_drain ();
			}
			else if (dev->max_count < dev->size)
			{
				/* Don't throw an enter event if the device thinks
				it is full: it has "locked" as many balls as it
				can hold.  The device code will throw a fatal if
				it sees this, which should never happen with real
				balls. */
				dbprintf ("Sim. enter dev %d\n", dev->devno);
				device_call_op (dev, enter);
				task_sleep (TIME_1S);
			}
		}
		else if (swinfo->flags & SW_PLAYFIELD)
		{
			/* Simulate the switch */
			tp = task_create_gid (GID_SW_HANDLER, switch_sched_task);
			task_set_arg (tp, sw);
		}
	}
}
コード例 #18
0
ファイル: multiball.c プロジェクト: SonnyJim/freewpc
bool can_lock_ball;
bool mb_jackpot_collected;
U8 multiball_level;
__local__ U8 balls_locked;

extern U8 mpf_level;
extern score_t total_jets_score;
extern U8 rightramp_level;
extern U8 left_loop_level;
extern U8 right_loop_level;
extern U8 total_body_parts;
score_t super_jackpot_calc;
score_t super_jackpot_value;


device_t *dev = device_entry (DEVNO_WIRE_BALL_LOCK);

void jackpot_deff (void)
{
	seg_alloc_clean ();
	seg_write_row_center (0, "JACKPOT");
	seg_write_row_center (1, "1 MILLION");
	seg_show ();
	task_sleep_sec (2);
	deff_exit ();
}

void super_jackpot_deff (void)
{
	seg_alloc_clean ();
	seg_write_row_center (0, "SUPER JACKPOT");
コード例 #19
0
ファイル: device.c プロジェクト: Dmilo/freewpc
/** Probes all devices to see if any balls are present that
 * shouldn't be.  Balls are kicked as necessary.
 *
 * For example, this clears out the balls of a lockup device
 * at the end of a game.
 *
 * This is always done as a background task, because it may
 * take some time and this function waits for any kicked
 * balls to successfully exit before it returns.
 */
void device_probe (void)
{
	devicenum_t devno;
	U8 kicks;

	dbprintf ("Probing devices\n");

	/* Keep track of the number of times we actually had to kick
	a device; equivalently, this is the number of times we sleep
	waiting for the kick to have an effect.  After so long, if
	things aren't right, just give up. */
	kicks = 0;

probe_from_beginning:
	if (kicks >= 10)
		goto probe_exit;

	for (devno = 0; devno < NUM_DEVICES; devno++)
	{
		device_t *dev = device_entry (devno);

		/* Recount the number of balls in the device, and reset
		 * other device data. */
		device_recount (dev);
		dev->previous_count = dev->actual_count;
		dev->kicks_needed = 0;
		dev->kick_errors = 0;
		task_kill_gid (DEVICE_GID(devno));

		/* If there are more balls in the device than ought to be,
		 * schedule the extras to be emptied.   Then rescan from
		 * the beginning again. */
		dev->max_count = dev->props->init_max_count;
		if (dev->actual_count > dev->max_count)
		{
			kicks++;
			device_request_kick (dev);
			task_sleep_sec (2);
			goto probe_from_beginning;
		}
#if 0 /* TODO */
		else if (dev->actual_count < dev->max_count)
		{
			/* The device normally holds more balls than are present in
			it.  If possible, launch a ball here.  (For example, ST:TNG
			or Whodunnit.) */
		}
#endif
	}

probe_exit:
	/* At this point, all kicks have been made, but balls may be
	on the playfield heading for the trough.  We still should wait
	until 'missing_balls' goes (hopefully) to zero.
	We'll wait for up to 10 seconds, but exit sooner if we find all
	balls before then. */
	task_sleep_sec (4);
	if (missing_balls != 0)
	{
		task_sleep_sec (2);
		if (missing_balls != 0)
		{
			task_sleep_sec (2);
			if (missing_balls != 0)
			{
				task_sleep_sec (2);
			}
		}
	}

	dbprintf ("Checking globals after probe\n");
	device_update_globals ();

	dbprintf ("\nDevices initialized.\n");
	device_debug_all ();
	task_exit ();
}
コード例 #20
0
ファイル: session.cpp プロジェクト: alepharchives/ULib
   void run(int argc, char* argv[], char* env[])
      {
      U_TRACE(5, "Application::run(%d,%p,%p)", argc, argv, env)

      UApplication::run(argc, argv, env);

      // LDAP attribute for devices
      // manage arg operation
      // manage file configuration
      // manage options
      // login to LDAP

#     include "common1.cpp"

      if (UApplication::isOptions())
         {
         value = opt['b'];

         if (value.empty() == false) DN_filter = value;
         }

      // get filter attribute from LDAP

      int i, n = ldap.search(DN_filter.c_str(), LDAP_SCOPE_BASE, (char**)filter_attr_name);

      if (n != 1) U_ERROR("cannot get filter attribute from LDAP...");

      ULDAPEntry filter_entry(FILTER_NUM_ATTR, filter_attr_name);

      ldap.get(filter_entry);

      // dato filtro avvio ricerca policy usando valore attributo filtro <tnetLrpFilterRule>

      const char* filtro = filter_entry.getCStr(FILTER_ATTR_CN_POS);
      const char* rule   = filter_entry.getCStr(FILTER_ATTR_RULE_POS);

      U_INTERNAL_DUMP("RULE = %S FILTRO = %S", rule, filtro)

      // get policy attribute from LDAP

      ULDAP ldap_url;
      char first_char = rule[0];
      bool policy_by_url = (first_char != '(');

      if (policy_by_url)
         {
         if (first_char == '/')
            {
            static char url[1024];

            (void) snprintf(url, sizeof(url), "ldap://%s%s", LDAP_host.c_str(), rule);

            rule = url;
            }

         if (ldap_url.init(rule) == false     ||
             ldap_url.set_protocol() == false ||
             ldap_url.simple_bind() == false)
            {
            U_ERROR("login to LDAP with URL failed...");
            }

         n = ldap_url.search();
         }
      else
         {
         n = ldap.search("o=Policies,o=tnet", LDAP_SCOPE_SUBTREE, (char**)policy_attr_name, rule);
         }

      if (n <= 0) U_ERROR("cannot find policy from LDAP...");

      ULDAPEntry policy_entry(POLICY_NUM_ATTR, policy_attr_name, n);

      if (policy_by_url) ldap_url.get(policy_entry);
      else                   ldap.get(policy_entry);

      // init log

      const char* log_name = filtro;

#     include "common2.cpp"

      // loop for every policy

      int j, k;
      char ipmask[64];
      const char* ptr;
      const char* policy;
      const char* ip_mask;
      const char* ip_device;
      char request_buffer[4096 * 4];
      const char* binddn_device = LDAP_binddn_device.c_str();

      for (i = 0; i < n; ++i)
         {
         policy = policy_entry.getCStr(POLICY_ATTR_CN_POS, i);

         U_INTERNAL_DUMP("POLICY = %S %S", policy_entry[i], policy)

         // data policy avvio ricerca lista device usando valore attributo policy <tnetLrpIpNetworkNumber>

         ip_mask = policy_entry.getCStr(POLICY_ATTR_IPMASK_POS, i);

         (void) snprintf(ipmask, sizeof(ipmask), "(tnetLrpIpHostNumber=%s*)", ip_mask);

         j = ldap.search(binddn_device, LDAP_SCOPE_SUBTREE, (char**)device_attr_name, ipmask);

         if (j <= 0) continue;

         ptr = policy_entry.getCStr(POLICY_ATTR_POLICY_POS, i);

         // check if to skip xml header...

         if (U_STRNEQ(ptr, "<?xml"))
            {
            ptr += 5;

            while (*ptr++ != '\n');
            }

         // build request

         static const char* request_tmpl = \
         // "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE REQUEST SYSTEM \"lrp_request.dtd\">"
            "<REQUEST sid=\"sid1\" version=\"1\"><IMPORT-POLICYLABEL  name=\"%s\">%s</IMPORT-POLICYLABEL></REQUEST>"
            "<REQUEST sid=\"sid2\" version=\"1\"><EXECUTE-POLICYLABEL name=\"%s\" command=\"%s\"/></REQUEST>";

         request_size = u__snprintf(request_buffer, sizeof(request_buffer), request_tmpl, policy, ptr, policy, operation);

         request = UString(request_buffer, request_size);

         // write request to file

#        include "common3.cpp"

         // set devices attribute for LDAP

         ULDAPEntry device_entry(DEVICE_NUM_ATTR, device_attr_name, j);

         // get devices attribute from LDAP
         // loop for every device
         // fork: child
         //  send request  to device
         // write response to file

#        include "common4.cpp"

         // parent
         }

      exit_value = proc.waitAll();

      log.close();
      }