using System; using System.Collections.Generic; using System.Linq; using Bridge; using Bridge.Html5; using Bridge.jQuery2; namespace WebMrbc { delegate bool jQueryKeyboardHandler(jQueryKeyboardEvent e); public static class App { public static Mruby Module; public static Terminal Term; public static AceEditor CodeEditor; public static bool changedAfterTranslating; public static bool translating; public static JsonClassInfo NodeProfileClass; public static JsonClassGroupInfo[] ClassGroups; public static JsonPropertyInfo[] BaseObjectPropertyList; public static string ArduinoToolbox; public static string EcnlToolbox; [Ready] public static void Main() { Script.Write("Number.isFinite = Number.isFinite || function(any) { return typeof any === 'number' && isFinite(any); }"); Script.Write("Number.isNaN = Number.isNaN || function(any) { return typeof any === 'number' && isNaN(any); }"); Collections.ClassWorkspaces = new Collection(); Views.ClassSelectorView = new ClassSelectorView(); Views.ClassSelectorView.SetCollection(Collections.ClassWorkspaces); Views.EObjectModalView = new EObjectModalView(); Views.MainMenuView = new MainMenuView(); var termElement = Document.GetElementById("term"); Term = new Terminal(new TerminalOption() { cols = 80, rows = 24, useStyle = true, screenKeys = true, cursorBlink = false }); Term.on("data", new Action((data) => { if (Module != null && Module["stdin"] != null) Module.stdin(data); })); Term.on("title", new Action((title) => { Document.Title = title; })); Term.open(termElement); CodeEditor = ace.edit("text-editor"); Window.Set("textEditor", CodeEditor); CodeEditor.setTheme("ace/theme/twilight"); CodeEditor.setShowInvisibles(true); CodeEditor.gotoLine(0, 0); CodeEditor.On("change", new Action(() => { if (!translating) { changedAfterTranslating = true; } })); var session = CodeEditor.getSession(); session.setMode("ace/mode/ruby"); session.setTabSize(2); session.setUseSoftTabs(false); var statusElement = Document.GetElementById("status"); var progressElement = Document.GetElementById("progress"); var spinnerElement = Document.GetElementById("spinner"); statusElement.InnerHTML = "Downloading..."; jQuery.Ready(() => { InitClassGroups((result) => { if (result) { Views.EObjectModalView.InitClassGroups(); } InitArduinoToolbox((result1) => { InitEcnlToolbox((result2) => { if (result1 && result2) { AddMainLoop(); AddEcnlTask(); AddENode(); } statusElement.InnerHTML = ""; spinnerElement.Style.Display = Display.None; }); }); }); }); } public static void InitClassGroups(Action action) { jQuery.Ajax(new AjaxOptions() { Url = "echonet_objects.json", Success = (data, textStatus, request) => { ClassGroupsSuccess(data, textStatus, request); action?.Invoke(true); }, Error = (request, textStatus, error) => { AjaxError(request, textStatus, error); action?.Invoke(false); } }); } private static void ClassGroupsSuccess(object data, string textStatus, jqXHR request) { var _deviceInfo = (dynamic)((data.GetType() == typeof(string)) ? jQuery.ParseJSON((string)data) : data); var profProps = new JsonPropertyInfo[0]; foreach (var property in _deviceInfo.baseProfilePropertyList) { profProps.Push(new JsonPropertyInfo(property)); } var objProps = new JsonPropertyInfo[0]; foreach (var property in _deviceInfo.baseObjectPropertyList) { objProps.Push(new JsonPropertyInfo(property)); } BaseObjectPropertyList = objProps; var classGroups = new JsonClassGroupInfo[0]; foreach (var _item in _deviceInfo.classGroupList) { var item = new JsonClassGroupInfo(_item); classGroups.Push(item); var classes = new JsonClassInfo[0]; foreach (var _cls in _item.classList) { var cls = new JsonClassInfo(_cls); cls.classGroup = item; classes.Push(cls); var properties = new JsonPropertyInfo[0]; foreach (var property in _cls.propertyList) { properties.Push(new JsonPropertyInfo(property)); } if ((item.classGroupCode == 0xE) && (cls.classCode == 0xF0)) { cls.properties = (JsonPropertyInfo[])profProps.Concat(properties); NodeProfileClass = cls; } else { cls.properties = properties; } } item.classes = classes; } ClassGroups = classGroups; } public static void InitArduinoToolbox(Action action) { jQuery.Ajax(new AjaxOptions() { Url = "arduino_toolbox.xml", DataType = "text", Success = (data, textStatus, request) => { ArduinoToolbox = (string)data; action?.Invoke(true); }, Error = (request, textStatus, error) => { AjaxError(request, textStatus, error); action?.Invoke(false); } }); } public static void InitEcnlToolbox(Action action) { jQuery.Ajax(new AjaxOptions() { Url = "ecnl_toolbox.xml", DataType = "text", Success = (data, textStatus, request) => { EcnlToolbox = (string)data; action?.Invoke(true); }, Error = (request, textStatus, error) => { AjaxError(request, textStatus, error); action?.Invoke(false); } }); } private static void AjaxError(jqXHR request, string textStatus, string error) { } /// /// テキスト入力欄のEnter(Return)キーを無視する /// /// /// static jQuery ignoreEnterKey(jQuery el) { return el.Find("input[type=text]").KeyPress(new jQueryKeyboardHandler((e) => { if (e == null) e = (jQueryKeyboardEvent)Window.Get("Event"); if (e.KeyCode == 13) return false; else return true; })); } private static void AddMainLoop() { var view = Views.MainMenuView.NewBlocklyView("MainLoop"); var workspace = new MainLoopWorkspace(view); Collections.MainLoopWorkspace = workspace; Collections.ClassWorkspaces.Add(workspace); Views.ClassSelectorView.SelectClassWorkspace(workspace); view.ReloadToolbox(workspace); } private static void AddEcnlTask() { var identifier = Collections.ClassWorkspaces.UniqueName("EcnlTask"); var view = Views.MainMenuView.NewBlocklyView(identifier); var workspace = new EcnlTaskWorkspace(view); Collections.EcnlTaskWorkspace = workspace; Collections.ClassWorkspaces.Add(workspace); Views.ClassSelectorView.SelectClassWorkspace(workspace); view.ReloadToolbox(workspace); } private static void AddENode() { var identifier = Collections.ClassWorkspaces.UniqueName("LocalNode"); var localNode = new JsonNodeInfo(NodeProfileClass, identifier, "local"); var properties = new JsonPropertyInfo[0]; foreach (var item in localNode.type.properties) { if ((item.required.Length > 0) && (item.required[0] != "NONE")) { properties.Push(new JsonPropertyInfo(item)); } } localNode.properties = properties; var view = Views.MainMenuView.NewBlocklyView(localNode.identifier); var workspace = new ENodeWorkspace(view, localNode); Collections.LocalNode = workspace; Collections.ClassWorkspaces.Add(workspace); Views.ClassSelectorView.SelectClassWorkspace(workspace); view.BlockCreated += workspace.OnBlockCreated; view.BlockDeleted += workspace.OnBlockDeleted; view.BlockChanged += workspace.OnBlockChanged; view.BlockMoveed += workspace.OnBlockMoveed; view.ReloadToolbox(workspace); } internal static void NewItem(Action callback) { var identifier = Collections.ClassWorkspaces.UniqueName("Kaden"); var eobject = new JsonObjectInfo(App.NodeProfileClass, identifier); var properties = new JsonPropertyInfo[0]; if ((eobject.type.classGroup.classGroupCode != 0x0E) && (eobject.type.classCode != 0xF0)) { foreach (var item in App.BaseObjectPropertyList) { if ((item.required.Length > 0) && (item.required[0] != "NONE")) { properties.Push(new JsonPropertyInfo(item)); } } } foreach (var item in eobject.type.properties) { if ((item.required.Length > 0) && (item.required[0] != "NONE")) { properties.Push(new JsonPropertyInfo(item)); } } eobject.properties = properties; var view = Views.MainMenuView.NewBlocklyView(eobject.identifier); var workspace = new EObjectWorkspace(view, eobject); view.BlockCreated += workspace.OnBlockCreated; view.BlockDeleted += workspace.OnBlockDeleted; view.BlockChanged += workspace.OnBlockChanged; view.BlockMoveed += workspace.OnBlockMoveed; callback(workspace); } internal static void RemoveItem(IClassWorkspace item) { Views.MainMenuView.RemoveEObjectWorkspace(item); } internal static void Write(string text) { Term?.write(text.Replace("\n", "\r\n")); } internal static void WriteLine(string text) { Term?.write(text.Replace("\n", "\r\n") + "\r\n"); } } public interface IClassWorkspace : IModel { Workspace Workspace { get; } BlocklyView View { get; } Ruby RubyCode { get; } string GetImageUrl(); bool IsPreset(); string ToCode(string filename); void Activate(); void Inactivate(); void ReloadToolbox(HTMLElement toolbox); void OpenModifyView(Action callback); string Template(string template); } public class Collections { [Name(false)] internal static ENodeWorkspace LocalNode; [Name(false)] internal static Collection ClassWorkspaces; [Name(false)] internal static EcnlTaskWorkspace EcnlTaskWorkspace; [Name(false)] internal static MainLoopWorkspace MainLoopWorkspace; } public class Views { [Name(false)] internal static MainMenuView MainMenuView; [Name(false)] internal static ClassSelectorView ClassSelectorView; [Name(false)] internal static EObjectModalView EObjectModalView; } public class HexDump { string text; public HexDump(Uint8Array bytes, int width) { var sb = new string[0]; for (int index = 0; index < bytes.Length; index += width) { sb.Push(String.Format("{0:X4} : ", index) + BinBump(bytes, index, width) + AsciiDump(bytes, index, width)); } text = sb.Join("\n"); } private string BinBump(Uint8Array bytes, int offset, int width) { var sb = new System.Text.StringBuilder(); for (int index = 0; index < width; index++) { if (index + offset < bytes.Length) { sb.AppendFormat("{0:X2} ", bytes[index + offset]); } else { sb.Append(" "); } } return sb.ToString(); } private string AsciiDump(Uint8Array bytes, int index, int width) { var sb = ""; if (index < bytes.Length) { width = System.Math.Min(width, bytes.Length - index); sb += ": "; for (int i = 0; i < width; i++) { byte b = bytes[i + index]; if (b < 0x20) sb += "\x1B[1;3;31m" + String.FromCharCode(b + 0x40) + "\x1B[0m"; else sb += String.FromCharCode(b); } } else { sb += ": "; } return sb; } public override string ToString() { return text; } } }