--Environment Code ------------------------------------------------------------------------------------- -- Yard Information if event.init then S.yards = { --[[ yard info template yard_id = { active_indicator_pos = POS(), dir_indicator_pos = POS(), error_indicator_pos = POS(), headshunt_max = number, }, ]]-- TY = { active_indicator_pos = POS(-4025,14,-2659), dir_indicator_pos = POS(-4025,13,-2665), error_indicator_pos = POS(-4025,13,-2671), headshunt_max = 5, }, BY = { active_indicator_pos = POS(-2002,3,-1099), dir_indicator_pos = POS(-2009,3,-1099), error_indicator_pos = POS(-1999,3,-1099), headshunt_max = 5, }, IP = { active_indicator_pos = POS(1179,16,3848), dir_indicator_pos = POS(1179,16,3855), error_indicator_pos = POS(1179,16,3850), headshunt_max = 5, }, -- HY = { -- active_indicator_pos = POS(-4025,14,-2659), -- dir_indicator_pos = POS(-4025,13,-2665), -- error_indicator_pos = POS(-4025,13,-2671), -- }, } end ------------------------------------------------------------------------------------ -- Utility Functions F.indicator = function(indicator,set) if set ~= nil then setstate(indicator,(set and "on") or "off") end return (getstate(indicator) == "on") or false end F.get_rc_safe = function() return get_rc() or "" end F.has_rc = function(query,rc_list) -- query = string, single entry if not atc_id then return false end if rc_list == "" or query == nil or query=="" then return false end if not rc_list then rc_list = F.get_rc_safe() end for word in rc_list:gmatch("[^%s]+") do if word == query then return true end end print(F.get_rc_safe()) return false end F.has_rc_match = function(query,rc_list) -- query = pattern string, single entry if not atc_id then return false end if rc_list == "" or query == nil or query=="" then return false end if not rc_list then rc_list = F.get_rc_safe() end local rc = {} for v in rc_list:gmatch("("..query..")") do table.insert(rc,v) end if rc[1] == true then return true, rc else return nil end end F.add_rc = function(rc_list) -- rc_list = string or table, eg: {"rc1","rc2"} OR "rc1 rc2" if not atc_id then return false end if type(rc_list) == "table" then rc_list = table.concat(rc_list," ") end set_rc(F.get_rc_safe().." "..rc_list) print(F.get_rc_safe()) return true end F.remove_rc = function(rc_list,arrow_mode) -- rc_list = string eg: "rc1 rc2 rc3" OR table eg: {"rc1","rc2","rc3"} -- Arrow Modes: -- true: with arrow direction -- false: against arrow direction -- nil: ignores arrow direction if not atc_id then return false end if not rc_list then return false end if (arrow_mode == nil) or (atc_arrow == arrow_mode) then -- prep rc_list to useable format local rc_remove = {} if type(rc_list) == "string" then for word in pairs(rc_list:gmatch("[^%s]+")) do rc_remove[word] = true end elseif type(rc_list) == "table" then for _,word in pairs(rc_list) do rc_remove[word] = true end end -- remove codes from train's rc local rc = F.get_rc_safe() local reinsert = {} for token in rc:gmatch("[^%s]+") do if not rc_remove[token] then table.insert(reinsert,token) end end -- insert new string to train's rc set_rc(table.concat(reinsert," ")) end print(F.get_rc_safe()) return reinsert end F.remove_rc_match = function(rc_list) -- rc_list = pattern string, single entry, eg: "rc_%d+" if not atc_id then return false end if not rc_list then return false end local rm = {} for v in F.get_rc_safe():gmatch("("..rc_list..")") do table.insert(rm,v) end F.remove_rc(rm) print(F.get_rc_safe()) return rm end ---------------------------------------------------------------------------------------------- -- Trackside Functions F.yard_arrival = function(yard_id,this_dir) -- arrow points towards yard local yard = S.yards[yard_id] --yard ref if F.has_rc(yard_id.."_NOSHUNT") then return end local function enter_yard() if not atc_id then F.indicator(yard.error_indicator_pos,true) end F.indicator(yard.dir_indicator_pos,this_dir) F.indicator(yard.active_indicator_pos,true) F.add_rc({yard_id.."_ARRIVE"}) if F.has_rc(yard_id.."_RTS") then --save the RTS flag as it's removed during the arrival procedure F.add_rc({yard_id.."_HAS_RTS"}) end atc_set_ars_disable(false) atc_send("S6") return end __approach_callback_mode = 1 if event.approach and not event.has_entered then atc_set_ars_disable(true) atc_set_lzb_tsr(1) return end if event.train then if F.indicator(yard.active_indicator_pos) then schedule_in(";10","recheck") return else enter_yard() return true end end if event.schedule then if F.indicator(yard.active_indicator_pos) then schedule_in(";10","recheck") return else enter_yard() return true end end end F.classification = function(yard_id, this_dir) -- arrow points towards headshunt local yard = S.yards[yard_id] --yard ref if not F.indicator(yard.active_indicator_pos) then return end if F.has_rc(yard_id.."_NOSHUNT") then return end -- this_dir == true for north end, false for south end if F.indicator(yard.active_indicator_pos) then if atc_arrow then -- loco is at working end F.remove_rc({yard_id.."_PICKUP"}) if F.has_rc(yard_id.."_ARRIVE") and F.indicator(yard.dir_indicator_pos) == this_dir then --first pass, prep train for working F.remove_rc({yard_id.."_AROUND"}) if not F.get_rc_safe():match(yard_id.."_LOCOS_%d+") or not F.get_rc_safe():match(yard_id.."_WAGONS_%d+") then local full_length = train_length() split_off_locomotive("A0B0") local loco_count = train_length() local wagon_count = 0 if full_length > loco_count then wagon_count = full_length - loco_count end F.add_rc({yard_id.."_LOCOS_"..loco_count, yard_id.."_WAGONS_"..wagon_count}) --[[ train will end up going through headshunt no matter what there shouldn't be any trains entering that pass straight through the yard any that do should run under the _NOSHUNT rc ]]-- if not F.has_rc(yard_id.."_RTS") then F.add_rc({yard_id.."_HEADSHUNT",yard_id.."_PICKUP"}) else F.indicator(yard.dir_indicator_pos,not F.indicator(yard.dir_indicator_pos)) F.remove_rc({yard_id.."_RTS"}) F.add_rc({yard_id.."_AROUND"}) -- send loco around to the other end atc_set_ars_disable(false) end return -- train has departed for headshunt/around end --else train has arrived from wagon count and headshunt/around F.remove_rc({yard_id.."_ARRIVE"}) step_fc() end if this_dir == F.indicator(yard.dir_indicator_pos) then --train has bounced and is ready to classify wagon(s) -- local pre_split = train_length() local lane = split_at_fc("A0B0",yard.headshunt_max) -- where to classify this rake -- headshunt length local locos = tonumber(F.get_rc_safe():match(yard_id.."_LOCOS_(%d+)")) or 1 --saved loco count local wagons = tonumber(F.get_rc_safe():match(yard_id.."_WAGONS_(%d+)")) or 0 -- wagon_count from last classification split local this_rake = train_length() - locos-- subtract these wagons from the overall wagon count if this_rake == wagons then -- mark for last_classification F.add_rc({yard_id.."_LAST_CLASS"}) end if lane ~= "" then F.add_rc({yard_id.."_CLASS_"..lane}) end F.remove_rc_match(yard_id.."_WAGONS_%d+") F.add_rc({yard_id.."_HEADSHUNT",yard_id.."_WAGONS_"..wagons-this_rake}) atc_set_ars_disable(false) set_autocouple() else --bounce train back towards working end atc_set_ars_disable(true) atc_send("S0WRD1S3") unset_autocouple() end else -- train entering from the far end. set autocouple so it pushes all the way through to the bounce set_autocouple() end end end F.headshunt_yard = function(yard_id,this_dir) -- arrow points toward yard local yard = S.yards[yard_id] --yard ref __approach_callback_mode = 1 if event.approach and not event.has_entered then atc_set_ars_disable(true) atc_set_lzb_tsr(1) return end if not F.indicator(yard.active_indicator_pos) then return end if F.has_rc(yard_id.."_NOSHUNT") then return end if event.train then if not atc_arrow then --train has entered headshunt from yard. bounce or depart atc_set_ars_disable(true) F.remove_rc({yard_id.."_HEADSHUNT"}) if not F.has_rc(yard_id.."_FINAL_COLLECT") then if not F.has_rc(yard_id.."_DEPART") then -- MOST TRAINS. only set when train wants to depart, otherwise train will start the reverse countdown schedule_in(";01",atc_id) return else if get_line() ~= "LHF" then -- LHF trains will always only collect the * lane for that direction. This code is for other trains local collect,next_collect = F.get_rc_safe():match(yard_id.."_COLLECT_(%S+)") if collect then --train needs to collect a rake from the yard before departing F.add_rc({yard_id.."_CLASS_"..collect}) if not next_collect then F.add_rc({yard_id.."_FINAL_COLLECT"}) end schedule_in(";01",atc_id) return end else -- CLASS_* should never be set in ARS, instead being left to the * route F.add_rc({yard_id.."_FINAL_COLLECT", yard_id.."_CLASS_*"}) schedule_in(";01",atc_id) return end end --else let train proceed forward to the exit controller without a rake end --else train has already collected rake. let it exit yard else --train has bounced if F.has_rc(yard_id.."_AROUND") then if this_dir == F.indicator(yard.dir_indicator_pos) then --send train to Classification F.remove_rc({yard_id.."_AROUND"}) F.add_rc({yard_id.."_PICKUP"}) atc_set_ars_disable(false) return end --if at wrong end, do nothing. train will follow TY_AROUND ARS to relevant headshunt end -- if doesn't have TY_AROUND then let ARS classify the train atc_set_ars_disable(false) end return end if event.schedule then if not atc_id then --bounce the train that just passed atc_send_to_train(event.msg,"B0WRD1S3A1") else if atc_id == event.msg then --train hasn't fully passed yet, wait a bit longer schedule_in(";01",atc_id) else -- somehow another train crossed the controller before the first one returned. Unlikely but possible atc_send_to_train(event.msg,"BBOL") atc_send("BBOL") F.indicator(yard.error_indicator_pos,true) end end return end end F.headshunt_exit = function(yard_id,this_dir) -- arrow points out of yard local yard = S.yards[yard_id] --yard ref __approach_callback_mode = 1 if event.approach and not event.has_entered then atc_set_ars_disable(true) atc_set_lzb_tsr(1) return end if not F.indicator(yard.active_indicator_pos) then return end if F.has_rc(yard_id.."_NOSHUNT") then return end if event.train then if atc_arrow and (F.indicator(yard.dir_indicator_pos) == this_dir) then if F.has_rc(yard_id.."_DEPART") then print("Train departs yard to mainline at full speed") unset_autocouple() F.remove_rc({yard_id.."_FINAL_COLLECT",yard_id.."_DEPART"}) F.remove_rc_match(yard_id.."_LOCOS_%d+") F.remove_rc_match(yard_id.."_WAGONS_%d+") if F.has_rc(yard_id.."_HAS_RTS") then --reinsert the RTS flag to fully return RC list to starting F.remove_rc(yard_id.."_HAS_RTS") F.add_rc({yard_id.."_RTS"}) end atc_set_ars_disable(false) -- set_route(exit_signal,"Exit Yard") atc_send("S6D10SM") schedule_in(";05","deactivate_check") -- wait a little to ensure train is clear from headshunt to deactivate yard print(F.get_rc_safe()) return end else atc_send("BBOL") F.indicator(yard.error_indicator_pos,true) end end if event.schedule then if event.msg == "deactivate_check" then if not atc_id then F.indicator(yard.active_indicator_pos,false) print("Yard Disabled") else schedule_in(";05","deactivate_check") return end end end end F.lane_EOL = function(yard_id,this_dir) -- arrow points towards headshunt local yard = S.yards[yard_id] --yard ref if not F.indicator(yard.active_indicator_pos) then return end if F.has_rc(yard_id.."_NOSHUNT") then return end if atc_arrow then if F.indicator(yard.dir_indicator_pos) == this_dir then --train has bounced and needs to leave the rake or depart with it if F.has_rc(yard_id.."_LAST_CLASS") then -- this is the last clasification move F.remove_rc({yard_id.."_LAST_CLASS"}) F.add_rc({yard_id.."_DEPART"}) split_off_locomotive("A0B0") elseif not F.has_rc(yard_id.."_FINAL_COLLECT") then -- disconnect loco and return to pickup split_off_locomotive("A0B0") F.add_rc({yard_id.."_PICKUP"}) end --if has FINAL_COLLECT then don't split off the loco. take the full rake F.add_rc({yard_id.."_HEADSHUNT"}) else --train needs to bounce atc_send("B0WRD1S4") end else if F.indicator(yard.dir_indicator_pos) == this_dir then --train is clasifying wagons, let it pass and couple to the rest of the rake F.remove_rc_match(yard_id.."_CLASS_%S+") else --this should never come into play as it means the train has entered from the wrong end somehow atc_send("BBOL") --stop the train and open the doors (if available) to signify assistance required F.indicator(yard.error_indicator_pos,true) end end end