From 853a9e690eeeb48aa1a13faa66db0a91d5b49390 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Wed, 4 Jan 2017 12:02:00 +0100 Subject: Add Automatic Train Control system --- advtrains/atc.lua | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 291 insertions(+), 1 deletion(-) (limited to 'advtrains/atc.lua') diff --git a/advtrains/atc.lua b/advtrains/atc.lua index 0afddb0..f070b20 100644 --- a/advtrains/atc.lua +++ b/advtrains/atc.lua @@ -1,4 +1,294 @@ --atc.lua --registers and controls the ATC system ---(simple)mesecon detector rails +local atc={} +-- ATC persistence table +atc.controllers = {} +--contents: {command="...", arrowconn=0-15 where arrow points} + +advtrains.fpath_atc=minetest.get_worldpath().."/advtrains_atc" +local file, err = io.open(advtrains.fpath_atc, "r") +if not file then + local er=err or "Unknown Error" + print("[advtrains]Failed loading advtrains atc save file "..er) +else + local tbl = minetest.deserialize(file:read("*a")) + if type(tbl) == "table" then + atc.controllers=tbl.controllers + end + file:close() +end +function atc.save() + --leave space for more save data. + local datastr = minetest.serialize({controllers = atc.controllers}) + if not datastr then + minetest.log("error", "[advtrains] Failed to serialize trackdb data!") + return + end + local file, err = io.open(advtrains.fpath_atc, "w") + if err then + return err + end + file:write(datastr) + file:close() +end + +--call from advtrains.detector subprogram + +function atc.trigger_controller_train_enter(pos, train_id) + atc.send_command(pos) +end + +--general + +function atc.send_command(pos) + local pts=minetest.pos_to_string(pos) + if atc.controllers[pts] then + print("Called send_command at "..pts) + local train_id = advtrains.detector.on_node[pts] + if train_id then + if advtrains.trains[train_id] then + print("send_command inside if: "..sid(train_id)) + atc.train_reset_command(train_id) + local arrowconn=atc.controllers[pts].arrowconn + local train=advtrains.trains[train_id] + for index, ppos in pairs(train.path) do + if vector.equals(advtrains.round_vector_floor_y(ppos), pos) then + advtrains.trains[train_id].atc_arrow = + vector.equals( + advtrains.dirCoordSet(pos, arrowconn), + advtrains.round_vector_floor_y(train.path[index+train.movedir]) + ) + advtrains.trains[train_id].atc_command=atc.controllers[pts].command + print("Sending ATC Command: "..atc.controllers[pts].command) + end + end + end + end + end + return false +end + +function atc.train_reset_command(train_id) + advtrains.trains[train_id].atc_command=nil + advtrains.trains[train_id].atc_delay=0 + advtrains.trains[train_id].atc_brake_target=nil + advtrains.trains[train_id].atc_wait_finish=nil + advtrains.trains[train_id].atc_arrow=nil +end + +--nodes +local idxtrans={static=1, mesecon=2, digiline=3} +local apn_func=function(pos) + advtrains.reset_trackdb_position(pos) + local meta=minetest.get_meta(pos) + if meta then + meta:set_string("infotext", "ATC controller, unconfigured.") + meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) + end +end + +advtrains.register_tracks("default", { + nodename_prefix="advtrains:dtrack_atc", + texture_prefix="advtrains_dtrack_atc", + models_prefix="advtrains_dtrack_detector", + models_suffix=".b3d", + shared_texture="advtrains_dtrack_rail_atc.png", + description="ATC controller", + formats={}, + get_additional_definiton = function(def, preset, suffix, rotation) + return { + after_place_node=apn_func, + on_place_rail=apn_func, + after_dig_node=function(pos) + advtrains.invalidate_all_paths() + advtrains.reset_trackdb_position(pos) + local pts=minetest.pos_to_string(pos) + atc.controllers[pts]=nil + end, + on_receive_fields = function(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + minetest.chat_send_player(player:get_player_name(), "This position is protected!") + return + end + local meta=minetest.get_meta(pos) + if meta then + if not fields.save then + --maybe only the dropdown changed + if fields.mode then + meta:set_string("mode", idxtrans[fields.mode]) + meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) ) + meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) + end + return + end + meta:set_string("mode", idxtrans[fields.mode]) + meta:set_string("command", fields.command) + meta:set_string("command_on", fields.command_on) + meta:set_string("channel", fields.channel) + meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) ) + meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) + + local pts=minetest.pos_to_string(pos) + local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) + atc.controllers[pts]={command=fields.command, arrowconn=conn1} + atc.send_command(pos) + end + end, + } + end +}, advtrains.trackpresets.t_30deg_straightonly) + + +function atc.get_atc_controller_formspec(pos, meta) + local mode=tonumber(meta:get_string("mode")) or 1 + local command=meta:get_string("command") + local command_on=meta:get_string("command_on") + local channel=meta:get_string("channel") + local formspec="size[8,6]".. + "dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]" + if mode<3 then + formspec=formspec.."field[0.5,1.5;7,1;command;Command;"..minetest.formspec_escape(command).."]" + if tonumber(mode)==2 then + formspec=formspec.."field[0.5,3;7,1;command_on;Command (on);"..minetest.formspec_escape(command_on).."]" + end + else + formspec=formspec.."field[0.5,1.5;7,1;channel;Digiline channel;"..minetest.formspec_escape(channel).."]" + end + return formspec.."button_exit[0.5,4.5;7,1;save;Save]" +end + +--from trainlogic.lua train step +local matchptn={ + ["SM"]=function(id, train) + train.tarvelocity=train.max_speed + return 2 + end, + ["S([0-9]+)"]=function(id, train, match) + train.tarvelocity=tonumber(match) + return #match+1 + end, + ["B([0-9]+)"]=function(id, train, match) + if train.velocity>tonumber(match) then + train.atc_brake_target=tonumber(match) + if train.tarvelocity>train.atc_brake_target then + train.tarvelocity=train.atc_brake_target + end + end + return #match+1 + end, + ["W"]=function(id, train) + train.atc_wait_finish=true + return 1 + end, + ["D([0-9]+)"]=function(id, train, match) + train.atc_delay=tonumber(match) + return #match+1 + end, + ["R"]=function(id, train) + if train.velocity<=0 then + train.movedir=train.movedir*-1 + train.atc_arrow = not train.atc_arrow + else + print("ATC Reverse command warning: didn't reverse train!") + end + return 1 + end, +} + +function atc.execute_atc_command(id, train) + --strip whitespaces + local command=string.match(train.atc_command, "^%s*(.*)$") + + + if string.match(command, "^%s*$") then + train.atc_command=nil + return + end + --conditional statement? + local is_cond, cond_applies + local cond, rest=string.match(command, "^I([%+%-])(.+)$") + if cond then + is_cond=true + if cond=="+" then + cond_applies=train.atc_arrow + end + if cond=="-" then + cond_applies=not train.atc_arrow + end + else + cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$") + if cond and compare then + is_cond=true + if cond=="<" then + cond_applies=train.velocity" then + cond_applies=train.velocity>tonumber(compare) + end + if cond=="<=" then + cond_applies=train.velocity<=tonumber(compare) + end + if cond==">=" then + cond_applies=train.velocity>=tonumber(compare) + end + end + end + if is_cond then + print("Evaluating if statement: "..command) + print("Cond: "..(cond or "nil")) + print("Applies: "..(cond_applies and "true" or "false")) + print("Rest: "..rest) + --find end of conditional statement + local nest, pos, elsepos=0, 1 + while nest>=0 do + if pos>#rest then + print("ATC command syntax error: I statement not closed: "..command) + atc.train_reset_command(id) + return + end + local char=string.sub(rest, pos, pos) + if char=="I" then + nest=nest+1 + end + if char==";" then + nest=nest-1 + end + if nest==0 and char=="E" then + elsepos=pos+0 + end + pos=pos+1 + end + if not elsepos then elsepos=pos-1 end + if cond_applies then + command=string.sub(rest, 1, elsepos-1)..string.sub(rest, pos) + else + command=string.sub(rest, elsepos+1, pos-2)..string.sub(rest, pos) + end + print("Result: "..command) + train.atc_command=command + atc.execute_atc_command(id, train) + return + else + for pattern, func in pairs(matchptn) do + local match=string.match(command, "^"..pattern) + if match then + local patlen=func(id, train, match) + + print("Executing: "..string.sub(command, 1, patlen)) + + train.atc_command=string.sub(command, patlen+1) + if train.atc_delay<=0 and not train.atc_wait_finish then + --continue (recursive, cmds shouldn't get too long, and it's a end-recursion.) + atc.execute_atc_command(id, train) + end + return + end + end + end + print("ATC command parse error: "..command) + atc.train_reset_command(id) +end + +--move table to desired place +advtrains.atc=atc -- cgit v1.2.3