#include "sim_device_io.h" #include #include #include static int valid_device(const sim_device_io *device) { return device != 0 && device->state.encoder_count > 0U && device->state.encoder_count <= SIM_DEVICE_MAX_ENCODERS && isfinite(device->cycle_time) && device->cycle_time > 0.0; } uint32_t sim_device_io_size(void) { return (uint32_t)sizeof(sim_device_io); } static int finite_value(double value) { return isfinite(value); } static void set_error(sim_device_io *device, int error) { if (device != 0) device->state.last_error = error; } static void update_safety(sim_device_io *device) { sim_device_safety_state *safety = &device->state.safety; safety->fault = (safety->door_open || !safety->input_ok) ? 1U : 0U; if (safety->fault || safety->estop) safety->latched_fault = 1U; safety->interlock_ok = (!safety->fault && !safety->estop && !safety->latched_fault) ? 1U : 0U; } static void update_coolant(sim_device_io *device) { sim_device_coolant_state *coolant = &device->state.coolant; const uint32_t allowed = device->state.safety.interlock_ok && !coolant->fault; coolant->mist = allowed ? coolant->mist_requested : 0U; coolant->flood = allowed ? coolant->flood_requested : 0U; coolant->lube = allowed ? coolant->lube_requested : 0U; } static void update_encoder(sim_device_io *device, uint32_t channel) { sim_device_encoder_state *encoder = &device->state.encoders[channel]; const double counts_per_revolution = (double)encoder->ppr * 4.0; const double delta = encoder->speed_rps * counts_per_revolution * device->cycle_time; int64_t raw; uint32_t phase; if (!encoder->registered) return; device->phase_accumulator[channel] += delta; raw = device->phase_accumulator[channel] >= 0.0 ? (int64_t)floor(device->phase_accumulator[channel]) : (int64_t)ceil(device->phase_accumulator[channel]); if (raw > INT32_MAX || raw < INT32_MIN) { encoder->raw_count = raw > 0 ? INT32_MAX : INT32_MIN; encoder->count = encoder->raw_count; set_error(device, SIM_DEVICE_ERR_RANGE); return; } encoder->raw_count = (int32_t)raw; encoder->count = encoder->raw_count; encoder->position = (double)encoder->raw_count / encoder->scale; encoder->velocity = encoder->speed_rps * counts_per_revolution / encoder->scale; phase = (uint32_t)(((int64_t)encoder->raw_count % 4LL + 4LL) % 4LL); encoder->phase_a = phase == 0U || phase == 1U; encoder->phase_b = phase == 1U || phase == 2U; encoder->index_pulse = 0U; if (encoder->index_enable && encoder->ppr > 0U && encoder->raw_count % (int32_t)counts_per_revolution == 0) { encoder->phase_z = 1U; encoder->index_pulse = 1U; encoder->index_enable = 0U; } else { encoder->phase_z = 0U; } encoder->tick = device->state.tick; } static int ack_index(const sim_device_io *device, uint32_t operation_id) { uint32_t index; for (index = 0; index < SIM_DEVICE_MAX_ACKS; ++index) if (device->state.acks[index].used && device->state.acks[index].operation_id == operation_id) return (int)index; return -1; } int sim_device_init(sim_device_io *device, uint32_t encoder_count, double cycle_time) { uint32_t index; if (device == 0 || encoder_count == 0U || encoder_count > SIM_DEVICE_MAX_ENCODERS || !finite_value(cycle_time) || cycle_time <= 0.0 || cycle_time > 10.0) return SIM_DEVICE_ERR_ARGUMENT; memset(device, 0, sizeof(*device)); device->cycle_time = cycle_time; device->state.encoder_count = encoder_count; device->state.last_error = SIM_DEVICE_OK; device->state.safety.input_ok = 1U; for (index = 0; index < encoder_count; ++index) { device->state.encoders[index].registered = 1U; device->state.encoders[index].ppr = 1000U; device->state.encoders[index].scale = 1.0; } update_safety(device); update_coolant(device); return SIM_DEVICE_OK; } int sim_device_reset(sim_device_io *device) { uint32_t count; double cycle; if (!valid_device(device)) return SIM_DEVICE_ERR_ARGUMENT; count = device->state.encoder_count; cycle = device->cycle_time; return sim_device_init(device, count, cycle); } int sim_device_encoder_set_speed(sim_device_io *device, uint32_t channel, double speed_rps) { if (!valid_device(device) || channel >= device->state.encoder_count) return SIM_DEVICE_ERR_ARGUMENT; if (!finite_value(speed_rps) || fabs(speed_rps) > 1000000.0) return SIM_DEVICE_ERR_NONFINITE; device->state.encoders[channel].speed_rps = speed_rps; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_encoder_set_config(sim_device_io *device, uint32_t channel, uint32_t ppr, double scale) { if (!valid_device(device) || channel >= device->state.encoder_count || ppr == 0U || ppr > 100000000U || !finite_value(scale) || scale <= 0.0) return SIM_DEVICE_ERR_RANGE; device->state.encoders[channel].ppr = ppr; device->state.encoders[channel].scale = scale; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_encoder_set_index_enable(sim_device_io *device, uint32_t channel, uint32_t enable) { if (!valid_device(device) || channel >= device->state.encoder_count || enable > 1U) return SIM_DEVICE_ERR_ARGUMENT; device->state.encoders[channel].index_enable = enable; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_coolant_command(sim_device_io *device, uint32_t mist, uint32_t flood, uint32_t lube) { if (!valid_device(device) || mist > 1U || flood > 1U || lube > 1U) return SIM_DEVICE_ERR_ARGUMENT; if ((mist || flood || lube) && !device->state.safety.interlock_ok) return SIM_DEVICE_ERR_INTERLOCK; device->state.coolant.mist_requested = mist; device->state.coolant.flood_requested = flood; device->state.coolant.lube_requested = lube; update_coolant(device); set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_coolant_set_fault(sim_device_io *device, uint32_t fault) { if (!valid_device(device) || fault > 1U) return SIM_DEVICE_ERR_ARGUMENT; device->state.coolant.fault = fault; update_coolant(device); set_error(device, fault ? SIM_DEVICE_ERR_STATE : SIM_DEVICE_OK); return fault ? SIM_DEVICE_ERR_STATE : SIM_DEVICE_OK; } int sim_device_door_set(sim_device_io *device, uint32_t open) { if (!valid_device(device) || open > 1U) return SIM_DEVICE_ERR_ARGUMENT; device->state.safety.door_open = open; update_safety(device); update_coolant(device); set_error(device, open ? SIM_DEVICE_ERR_INTERLOCK : SIM_DEVICE_OK); return open ? SIM_DEVICE_ERR_INTERLOCK : SIM_DEVICE_OK; } int sim_device_interlock_set(sim_device_io *device, uint32_t input_ok) { if (!valid_device(device) || input_ok > 1U) return SIM_DEVICE_ERR_ARGUMENT; device->state.safety.input_ok = input_ok; update_safety(device); update_coolant(device); set_error(device, input_ok ? SIM_DEVICE_OK : SIM_DEVICE_ERR_INTERLOCK); return input_ok ? SIM_DEVICE_OK : SIM_DEVICE_ERR_INTERLOCK; } int sim_device_interlock_reset(sim_device_io *device) { if (!valid_device(device)) return SIM_DEVICE_ERR_ARGUMENT; update_safety(device); if (device->state.safety.door_open || !device->state.safety.input_ok || device->state.safety.estop) return SIM_DEVICE_ERR_INTERLOCK; device->state.safety.latched_fault = 0U; device->state.safety.recovery_epoch += 1U; update_safety(device); update_coolant(device); set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_estop_set(sim_device_io *device, uint32_t estop) { if (!valid_device(device) || estop > 1U) return SIM_DEVICE_ERR_ARGUMENT; device->state.safety.estop = estop; update_safety(device); update_coolant(device); set_error(device, estop ? SIM_DEVICE_ERR_INTERLOCK : SIM_DEVICE_OK); return estop ? SIM_DEVICE_ERR_INTERLOCK : SIM_DEVICE_OK; } int sim_device_ack_issue(sim_device_io *device, uint32_t operation_id, uint32_t timeout_ticks) { uint32_t index; if (!valid_device(device) || operation_id == 0U || timeout_ticks == 0U || timeout_ticks > 1000000U) return SIM_DEVICE_ERR_ARGUMENT; if (ack_index(device, operation_id) >= 0) return SIM_DEVICE_ERR_DUPLICATE; for (index = 0; index < SIM_DEVICE_MAX_ACKS; ++index) if (!device->state.acks[index].used) break; if (index == SIM_DEVICE_MAX_ACKS) return SIM_DEVICE_ERR_CAPACITY; device->state.acks[index].used = 1U; device->state.acks[index].operation_id = operation_id; device->state.acks[index].issued_tick = device->state.tick; device->state.acks[index].deadline_tick = device->state.tick + timeout_ticks; device->state.acks[index].status = SIM_DEVICE_ACK_PENDING; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_ack_ack(sim_device_io *device, uint32_t operation_id) { int index; if (!valid_device(device)) return SIM_DEVICE_ERR_ARGUMENT; index = ack_index(device, operation_id); if (index < 0) return SIM_DEVICE_ERR_MISSING; if (device->state.acks[index].status == SIM_DEVICE_ACK_TIMED_OUT) return SIM_DEVICE_ERR_LATE_ACK; if (device->state.acks[index].status != SIM_DEVICE_ACK_PENDING) return SIM_DEVICE_ERR_STATE; device->state.acks[index].status = SIM_DEVICE_ACK_ACKED; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_ack_abort(sim_device_io *device, uint32_t operation_id) { int index; if (!valid_device(device)) return SIM_DEVICE_ERR_ARGUMENT; index = ack_index(device, operation_id); if (index < 0) return SIM_DEVICE_ERR_MISSING; if (device->state.acks[index].status == SIM_DEVICE_ACK_ACKED) return SIM_DEVICE_ERR_STATE; device->state.acks[index].status = SIM_DEVICE_ACK_ABORTED; set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_ack_step(sim_device_io *device, uint32_t ticks) { uint32_t index, tick; if (!valid_device(device) || ticks > 1000000U || UINT32_MAX - device->state.tick < ticks) return SIM_DEVICE_ERR_RANGE; for (tick = 0; tick < ticks; ++tick) { device->state.tick += 1U; for (index = 0; index < SIM_DEVICE_MAX_ACKS; ++index) if (device->state.acks[index].used && device->state.acks[index].status == SIM_DEVICE_ACK_PENDING && device->state.tick >= device->state.acks[index].deadline_tick) device->state.acks[index].status = SIM_DEVICE_ACK_TIMED_OUT; } return SIM_DEVICE_OK; } int sim_device_step(sim_device_io *device, uint32_t ticks) { sim_device_io before; uint32_t tick, index; if (!valid_device(device) || ticks > 1000000U || UINT32_MAX - device->state.tick < ticks) return SIM_DEVICE_ERR_RANGE; memcpy(&before, device, sizeof(before)); for (tick = 0; tick < ticks; ++tick) { device->state.tick += 1U; update_safety(device); update_coolant(device); for (index = 0; index < device->state.encoder_count; ++index) update_encoder(device, index); for (index = 0; index < SIM_DEVICE_MAX_ACKS; ++index) if (device->state.acks[index].used && device->state.acks[index].status == SIM_DEVICE_ACK_PENDING && device->state.tick >= device->state.acks[index].deadline_tick) device->state.acks[index].status = SIM_DEVICE_ACK_TIMED_OUT; } if (device->state.last_error == SIM_DEVICE_ERR_RANGE) { memcpy(device, &before, sizeof(before)); return SIM_DEVICE_ERR_RANGE; } set_error(device, SIM_DEVICE_OK); return SIM_DEVICE_OK; } int sim_device_read_state(const sim_device_io *device, sim_device_io_state *state) { if (!valid_device(device) || state == 0) return SIM_DEVICE_ERR_ARGUMENT; *state = device->state; return SIM_DEVICE_OK; } static uint64_t fnv1a(const unsigned char *bytes, size_t length) { uint64_t hash = UINT64_C(1469598103934665603); size_t index; for (index = 0; index < length; ++index) { hash ^= bytes[index]; hash *= UINT64_C(1099511628211); } return hash; } uint64_t sim_device_state_hash(const sim_device_io *device) { unsigned char bytes[sizeof(sim_device_io_state) + sizeof(double) * SIM_DEVICE_MAX_ENCODERS]; if (!valid_device(device)) return 0; memcpy(bytes, &device->state, sizeof(device->state)); memcpy(bytes + sizeof(device->state), device->phase_accumulator, sizeof(device->phase_accumulator)); return fnv1a(bytes, sizeof(bytes)); } uint32_t sim_device_canonical(const sim_device_io *device, char *output, uint32_t capacity) { char discard[1]; char *destination = output == 0 ? discard : output; uint32_t destination_capacity = output == 0 ? 1U : capacity; int written; uint32_t index; if (!valid_device(device)) return 0; written = snprintf(destination, destination_capacity, "{\"coolant\":{\"flood\":%u,\"lube\":%u,\"mist\":%u},\"encoders\":[", device->state.coolant.flood, device->state.coolant.lube, device->state.coolant.mist); if (written < 0) return 0; for (index = 0; index < device->state.encoder_count; ++index) { const sim_device_encoder_state *encoder = &device->state.encoders[index]; int part = snprintf(destination + (written < (int)destination_capacity ? written : 0), written < (int)destination_capacity ? destination_capacity - (uint32_t)written : 0U, "%s{\"count\":%d,\"index_pulse\":%u,\"position\":%.9g,\"velocity\":%.9g}", index ? "," : "", encoder->count, encoder->index_pulse, encoder->position, encoder->velocity); if (part < 0) return 0; written += part; } { int part = snprintf(destination + (written < (int)destination_capacity ? written : 0), written < (int)destination_capacity ? destination_capacity - (uint32_t)written : 0U, "],\"safety\":{\"door_open\":%u,\"estop\":%u,\"interlock_ok\":%u,\"latched_fault\":%u},\"tick\":%u}", device->state.safety.door_open, device->state.safety.estop, device->state.safety.interlock_ok, device->state.safety.latched_fault, device->state.tick); if (part < 0) return 0; written += part; } return (uint32_t)written + 1U; }