/* Insert an unbound ball into a node. */ void node_insert (struct ball_node *node, struct ball *ball) { unsigned int offset; if (ball->node) { simlog (SLC_DEBUG, "node_insert: %s already at %s", ball->name, ball->node->name); return; } if (node_full_p (node) || !node->type) { simlog (SLC_DEBUG, "node_insert: %s not allowed", node->name); return; } offset = (node->head + node->count) % node->size; node->ball_queue[offset] = ball; node->count++; ball->node = node; ball->pos = offset; if (node->type->insert) node->type->insert (node, ball); #ifdef CONFIG_UI ui_update_ball_tracker (ball->index, node->name); #endif simlog (SLC_DEBUG, "node_insert: added %s to %s, count=%d", ball->name, node->name, node->count); if (node->unlocked && !node_full_p (node->next)) sim_time_register (100, FALSE, (time_handler_t)node_kick_delayed, node); }
/* Insert a ball at a node after some number of milliseconds has expired. Until then the ball is not attached to any node. */ void node_insert_delay (struct ball_node *dst, struct ball *ball, unsigned int delay) { ball->node = dst; ball->timer = delay; sim_time_register (MIN_DELAY, FALSE, (time_handler_t)node_insert_delay_update, ball); }
/** * Update the state machine for a coil. * * The complexity here is that software can pulse the solenoid rapidly * between on and off; the goal is to detect when a single 'kick' operation * has occurred. That requires ignoring brief off-periods and only * considering that it is been on a majority of the time in the recent past. * * Also, once a kick has occurred, it is not considered to kick again until * it has returned to the resting position (in reality, to allow another ball * to enter the kicking area). * * This function uses periodic callbacks to keep the solenoid state updated * even when no writes are emitted from the CPU; the power driver board * latches state. */ static void sim_coil_update (struct sim_coil_state *c) { struct sim_coil_state *m = c->master; if (c->master == NULL) return; /* If the IO is on and the coil has not reached its maximum, move it forward. */ if (c->on && m->pos < m->type->max_pos) { m->pos += c->type->on_step; if (m->pos >= m->type->max_pos && !m->at_max) { m->pos = m->type->max_pos; m->at_max = 1; simlog (SLC_DEBUG, "Coil %d on", m - coil_states); if (m->type->at_max) m->type->at_max (c); } } /* Else, if the IO is off and the coil has not reached its rest state, move it backward. */ else if (!c->on && m->pos > 0) { m->pos += c->type->off_step; if (m->pos <= 0) { m->pos = 0; m->at_max = 0; simlog (SLC_DEBUG, "Coil %d off", m - coil_states); if (m->type->at_rest) m->type->at_rest (c); } } if (c->chain) sim_coil_update (c->chain); #if 0 simlog (SLC_DEBUG, "Coil %d on=%d pos=%d of %d", m - coil_states, c->on, m->pos, m->type->max_pos); #endif /* If the coil requires monitoring, and it is not at rest, then reschedule. Else, we are done until the CPU modifies it again. */ if (!c->type->unmonitored && m->pos != 0) sim_time_register (1, FALSE, (time_handler_t)sim_coil_update, c); else { c->scheduled = 0; } }
void node_insert_delay_update (struct ball *ball) { ball->timer -= MIN_DELAY; if (ball->timer <= 0) { struct ball_node *dst = ball->node; ball->node = NULL; node_insert (dst, ball); } else sim_time_register (MIN_DELAY, FALSE, (time_handler_t)node_insert_delay_update, ball); }
/** * Called when the CPU board writes to a solenoid signal. * coil identifies the signal; on is nonzero for active voltage. */ void sim_coil_change (unsigned int coil, unsigned int on) { struct sim_coil_state *c = coil_states + coil; if (c->disabled) return; if (c->on != on) { c->on = on; if (!c->scheduled) { sim_time_register (1, FALSE, (time_handler_t)sim_coil_update, c); c->scheduled = 1; } } }
void ui_init (void) { sim_time_register (PS_FREQ, TRUE, remote_msg_update, NULL); }