-- Experiment-level phase config tables -- One row per experiment per phase; linked by experiment_id. Used when creating an experiment -- so soaking, airdrying, cracking, and shelling parameters are stored and can be applied to repetitions. -- ============================================= -- 1. EXPERIMENT_SOAKING (template for soaking phase) -- ============================================= CREATE TABLE IF NOT EXISTS public.experiment_soaking ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), experiment_id UUID NOT NULL REFERENCES public.experiments(id) ON DELETE CASCADE, soaking_duration_hr DOUBLE PRECISION NOT NULL CHECK (soaking_duration_hr >= 0), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL REFERENCES public.user_profiles(id), CONSTRAINT unique_experiment_soaking_per_experiment UNIQUE (experiment_id) ); CREATE INDEX IF NOT EXISTS idx_experiment_soaking_experiment_id ON public.experiment_soaking(experiment_id); GRANT ALL ON public.experiment_soaking TO authenticated; ALTER TABLE public.experiment_soaking ENABLE ROW LEVEL SECURITY; CREATE POLICY "Experiment soaking config is viewable by authenticated" ON public.experiment_soaking FOR SELECT USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment soaking config is insertable by authenticated" ON public.experiment_soaking FOR INSERT WITH CHECK (auth.role() = 'authenticated'); CREATE POLICY "Experiment soaking config is updatable by authenticated" ON public.experiment_soaking FOR UPDATE USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment soaking config is deletable by authenticated" ON public.experiment_soaking FOR DELETE USING (auth.role() = 'authenticated'); CREATE TRIGGER set_updated_at_experiment_soaking BEFORE UPDATE ON public.experiment_soaking FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); -- ============================================= -- 2. EXPERIMENT_AIRDRYING (template for airdrying phase) -- ============================================= CREATE TABLE IF NOT EXISTS public.experiment_airdrying ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), experiment_id UUID NOT NULL REFERENCES public.experiments(id) ON DELETE CASCADE, duration_minutes INTEGER NOT NULL CHECK (duration_minutes >= 0), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL REFERENCES public.user_profiles(id), CONSTRAINT unique_experiment_airdrying_per_experiment UNIQUE (experiment_id) ); CREATE INDEX IF NOT EXISTS idx_experiment_airdrying_experiment_id ON public.experiment_airdrying(experiment_id); GRANT ALL ON public.experiment_airdrying TO authenticated; ALTER TABLE public.experiment_airdrying ENABLE ROW LEVEL SECURITY; CREATE POLICY "Experiment airdrying config is viewable by authenticated" ON public.experiment_airdrying FOR SELECT USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment airdrying config is insertable by authenticated" ON public.experiment_airdrying FOR INSERT WITH CHECK (auth.role() = 'authenticated'); CREATE POLICY "Experiment airdrying config is updatable by authenticated" ON public.experiment_airdrying FOR UPDATE USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment airdrying config is deletable by authenticated" ON public.experiment_airdrying FOR DELETE USING (auth.role() = 'authenticated'); CREATE TRIGGER set_updated_at_experiment_airdrying BEFORE UPDATE ON public.experiment_airdrying FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); -- ============================================= -- 3. EXPERIMENT_CRACKING (template for cracking; supports JC and Meyer params) -- ============================================= CREATE TABLE IF NOT EXISTS public.experiment_cracking ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), experiment_id UUID NOT NULL REFERENCES public.experiments(id) ON DELETE CASCADE, machine_type_id UUID NOT NULL REFERENCES public.machine_types(id) ON DELETE RESTRICT, -- JC Cracker parameters (nullable; used when machine is JC) plate_contact_frequency_hz DOUBLE PRECISION CHECK (plate_contact_frequency_hz IS NULL OR plate_contact_frequency_hz > 0), throughput_rate_pecans_sec DOUBLE PRECISION CHECK (throughput_rate_pecans_sec IS NULL OR throughput_rate_pecans_sec > 0), crush_amount_in DOUBLE PRECISION CHECK (crush_amount_in IS NULL OR crush_amount_in >= 0), entry_exit_height_diff_in DOUBLE PRECISION, -- Meyer Cracker parameters (nullable; used when machine is Meyer) motor_speed_hz DOUBLE PRECISION CHECK (motor_speed_hz IS NULL OR motor_speed_hz > 0), jig_displacement_inches DOUBLE PRECISION CHECK (jig_displacement_inches IS NULL OR jig_displacement_inches >= 0), spring_stiffness_nm DOUBLE PRECISION CHECK (spring_stiffness_nm IS NULL OR spring_stiffness_nm > 0), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL REFERENCES public.user_profiles(id), CONSTRAINT unique_experiment_cracking_per_experiment UNIQUE (experiment_id) ); CREATE INDEX IF NOT EXISTS idx_experiment_cracking_experiment_id ON public.experiment_cracking(experiment_id); CREATE INDEX IF NOT EXISTS idx_experiment_cracking_machine_type_id ON public.experiment_cracking(machine_type_id); GRANT ALL ON public.experiment_cracking TO authenticated; ALTER TABLE public.experiment_cracking ENABLE ROW LEVEL SECURITY; CREATE POLICY "Experiment cracking config is viewable by authenticated" ON public.experiment_cracking FOR SELECT USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment cracking config is insertable by authenticated" ON public.experiment_cracking FOR INSERT WITH CHECK (auth.role() = 'authenticated'); CREATE POLICY "Experiment cracking config is updatable by authenticated" ON public.experiment_cracking FOR UPDATE USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment cracking config is deletable by authenticated" ON public.experiment_cracking FOR DELETE USING (auth.role() = 'authenticated'); CREATE TRIGGER set_updated_at_experiment_cracking BEFORE UPDATE ON public.experiment_cracking FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); -- ============================================= -- 4. EXPERIMENT_SHELLING (template for shelling phase) -- ============================================= CREATE TABLE IF NOT EXISTS public.experiment_shelling ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), experiment_id UUID NOT NULL REFERENCES public.experiments(id) ON DELETE CASCADE, ring_gap_inches NUMERIC(6,2) CHECK (ring_gap_inches IS NULL OR ring_gap_inches > 0), drum_rpm INTEGER CHECK (drum_rpm IS NULL OR drum_rpm > 0), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL REFERENCES public.user_profiles(id), CONSTRAINT unique_experiment_shelling_per_experiment UNIQUE (experiment_id) ); CREATE INDEX IF NOT EXISTS idx_experiment_shelling_experiment_id ON public.experiment_shelling(experiment_id); GRANT ALL ON public.experiment_shelling TO authenticated; ALTER TABLE public.experiment_shelling ENABLE ROW LEVEL SECURITY; CREATE POLICY "Experiment shelling config is viewable by authenticated" ON public.experiment_shelling FOR SELECT USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment shelling config is insertable by authenticated" ON public.experiment_shelling FOR INSERT WITH CHECK (auth.role() = 'authenticated'); CREATE POLICY "Experiment shelling config is updatable by authenticated" ON public.experiment_shelling FOR UPDATE USING (auth.role() = 'authenticated'); CREATE POLICY "Experiment shelling config is deletable by authenticated" ON public.experiment_shelling FOR DELETE USING (auth.role() = 'authenticated'); CREATE TRIGGER set_updated_at_experiment_shelling BEFORE UPDATE ON public.experiment_shelling FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();