source: EcnlProtoTool/trunk/webapp/webmrbc/App.cs@ 270

Last change on this file since 270 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csharp
File size: 11.0 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using Bridge;
5using Bridge.Html5;
6using Bridge.jQuery2;
7
8namespace WebMrbc
9{
10 delegate bool jQueryKeyboardHandler(jQueryKeyboardEvent e);
11
12 public static class App
13 {
14 public static Mruby Module;
15 public static Terminal Term;
16 public static AceEditor CodeEditor;
17 public static bool changedAfterTranslating;
18 public static bool translating;
19 public static JsonClassInfo NodeProfileClass;
20 public static JsonClassGroupInfo[] ClassGroups;
21 public static JsonPropertyInfo[] BaseObjectPropertyList;
22 public static string ArduinoToolbox;
23 public static string EcnlToolbox;
24
25 [Ready]
26 public static void Main()
27 {
28 Script.Write("Number.isFinite = Number.isFinite || function(any) { return typeof any === 'number' && isFinite(any); }");
29 Script.Write("Number.isNaN = Number.isNaN || function(any) { return typeof any === 'number' && isNaN(any); }");
30
31 Collections.ClassWorkspaces = new Collection<IClassWorkspace>();
32 Views.ClassSelectorView = new ClassSelectorView();
33 Views.ClassSelectorView.SetCollection(Collections.ClassWorkspaces);
34 Views.EObjectModalView = new EObjectModalView();
35 Views.MainMenuView = new MainMenuView();
36
37 var termElement = Document.GetElementById("term");
38 Term = new Terminal(new TerminalOption() {
39 cols = 80,
40 rows = 24,
41 useStyle = true,
42 screenKeys = true,
43 cursorBlink = false
44 });
45 Term.on("data", new Action<object>((data) => {
46 if (Module != null && Module["stdin"] != null)
47 Module.stdin(data);
48 }));
49 Term.on("title", new Action<string>((title) => {
50 Document.Title = title;
51 }));
52
53 Term.open(termElement);
54
55 CodeEditor = ace.edit("text-editor");
56 Window.Set("textEditor", CodeEditor);
57 CodeEditor.setTheme("ace/theme/twilight");
58 CodeEditor.setShowInvisibles(true);
59 CodeEditor.gotoLine(0, 0);
60 CodeEditor.On("change", new Action(() => {
61 if (!translating) {
62 changedAfterTranslating = true;
63 }
64 }));
65 var session = CodeEditor.getSession();
66 session.setMode("ace/mode/ruby");
67 session.setTabSize(2);
68 session.setUseSoftTabs(false);
69
70 var statusElement = Document.GetElementById("status");
71 var progressElement = Document.GetElementById("progress");
72 var spinnerElement = Document.GetElementById("spinner");
73
74 statusElement.InnerHTML = "Downloading...";
75
76 jQuery.Ready(() => {
77 InitClassGroups((result) => {
78 if (result) {
79 Views.EObjectModalView.InitClassGroups();
80 }
81 InitArduinoToolbox((result1) => {
82 InitEcnlToolbox((result2) => {
83 if (result1 && result2) {
84 AddMainLoop();
85 AddEcnlTask();
86 AddENode();
87 }
88 statusElement.InnerHTML = "";
89 spinnerElement.Style.Display = Display.None;
90 });
91 });
92 });
93 });
94 }
95
96 public static void InitClassGroups(Action<bool> action)
97 {
98 jQuery.Ajax(new AjaxOptions() {
99 Url = "echonet_objects.json",
100 Success = (data, textStatus, request) => {
101 ClassGroupsSuccess(data, textStatus, request);
102 action?.Invoke(true);
103 },
104 Error = (request, textStatus, error) => {
105 AjaxError(request, textStatus, error);
106 action?.Invoke(false);
107 }
108 });
109 }
110
111 private static void ClassGroupsSuccess(object data, string textStatus, jqXHR request)
112 {
113 var _deviceInfo = (dynamic)((data.GetType() == typeof(string)) ? jQuery.ParseJSON((string)data) : data);
114 var profProps = new JsonPropertyInfo[0];
115 foreach (var property in _deviceInfo.baseProfilePropertyList) {
116 profProps.Push(new JsonPropertyInfo(property));
117 }
118 var objProps = new JsonPropertyInfo[0];
119 foreach (var property in _deviceInfo.baseObjectPropertyList) {
120 objProps.Push(new JsonPropertyInfo(property));
121 }
122 BaseObjectPropertyList = objProps;
123
124 var classGroups = new JsonClassGroupInfo[0];
125 foreach (var _item in _deviceInfo.classGroupList) {
126 var item = new JsonClassGroupInfo(_item);
127 classGroups.Push(item);
128 var classes = new JsonClassInfo[0];
129 foreach (var _cls in _item.classList) {
130 var cls = new JsonClassInfo(_cls);
131 cls.classGroup = item;
132 classes.Push(cls);
133 var properties = new JsonPropertyInfo[0];
134 foreach (var property in _cls.propertyList) {
135 properties.Push(new JsonPropertyInfo(property));
136 }
137 if ((item.classGroupCode == 0xE) && (cls.classCode == 0xF0)) {
138 cls.properties = (JsonPropertyInfo[])profProps.Concat(properties);
139 NodeProfileClass = cls;
140 }
141 else {
142 cls.properties = properties;
143 }
144 }
145 item.classes = classes;
146 }
147 ClassGroups = classGroups;
148 }
149
150 public static void InitArduinoToolbox(Action<bool> action)
151 {
152 jQuery.Ajax(new AjaxOptions() {
153 Url = "arduino_toolbox.xml",
154 DataType = "text",
155 Success = (data, textStatus, request) => {
156 ArduinoToolbox = (string)data;
157 action?.Invoke(true);
158 },
159 Error = (request, textStatus, error) => {
160 AjaxError(request, textStatus, error);
161 action?.Invoke(false);
162 }
163 });
164 }
165
166 public static void InitEcnlToolbox(Action<bool> action)
167 {
168 jQuery.Ajax(new AjaxOptions() {
169 Url = "ecnl_toolbox.xml",
170 DataType = "text",
171 Success = (data, textStatus, request) => {
172 EcnlToolbox = (string)data;
173 action?.Invoke(true);
174 },
175 Error = (request, textStatus, error) => {
176 AjaxError(request, textStatus, error);
177 action?.Invoke(false);
178 }
179 });
180 }
181
182 private static void AjaxError(jqXHR request, string textStatus, string error)
183 {
184 }
185
186 /// <summary>
187 /// テキスト入力欄のEnter(Return)キーを無視する
188 /// </summary>
189 /// <param name="el"></param>
190 /// <returns></returns>
191 static jQuery ignoreEnterKey(jQuery el)
192 {
193 return el.Find("input[type=text]").KeyPress(new jQueryKeyboardHandler((e) => {
194 if (e == null)
195 e = (jQueryKeyboardEvent)Window.Get("Event");
196 if (e.KeyCode == 13)
197 return false;
198 else
199 return true;
200 }));
201 }
202
203 private static void AddMainLoop()
204 {
205 var view = Views.MainMenuView.NewBlocklyView("MainLoop");
206 var workspace = new MainLoopWorkspace(view);
207 Collections.MainLoopWorkspace = workspace;
208 Collections.ClassWorkspaces.Add(workspace);
209 Views.ClassSelectorView.SelectClassWorkspace(workspace);
210 view.ReloadToolbox(workspace);
211 }
212
213 private static void AddEcnlTask()
214 {
215 var identifier = Collections.ClassWorkspaces.UniqueName("EcnlTask");
216 var view = Views.MainMenuView.NewBlocklyView(identifier);
217 var workspace = new EcnlTaskWorkspace(view);
218 Collections.EcnlTaskWorkspace = workspace;
219 Collections.ClassWorkspaces.Add(workspace);
220 Views.ClassSelectorView.SelectClassWorkspace(workspace);
221 view.ReloadToolbox(workspace);
222 }
223
224 private static void AddENode()
225 {
226 var identifier = Collections.ClassWorkspaces.UniqueName("LocalNode");
227 var localNode = new JsonNodeInfo(NodeProfileClass, identifier, "local");
228 var properties = new JsonPropertyInfo[0];
229 foreach (var item in localNode.type.properties) {
230 if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
231 properties.Push(new JsonPropertyInfo(item));
232 }
233 }
234 localNode.properties = properties;
235 var view = Views.MainMenuView.NewBlocklyView(localNode.identifier);
236 var workspace = new ENodeWorkspace(view, localNode);
237 Collections.LocalNode = workspace;
238 Collections.ClassWorkspaces.Add(workspace);
239 Views.ClassSelectorView.SelectClassWorkspace(workspace);
240 view.BlockCreated += workspace.OnBlockCreated;
241 view.BlockDeleted += workspace.OnBlockDeleted;
242 view.BlockChanged += workspace.OnBlockChanged;
243 view.BlockMoveed += workspace.OnBlockMoveed;
244 view.ReloadToolbox(workspace);
245 }
246
247 internal static void NewItem(Action<IClassWorkspace> callback)
248 {
249 var identifier = Collections.ClassWorkspaces.UniqueName("Kaden");
250 var eobject = new JsonObjectInfo(App.NodeProfileClass, identifier);
251 var properties = new JsonPropertyInfo[0];
252 if ((eobject.type.classGroup.classGroupCode != 0x0E)
253 && (eobject.type.classCode != 0xF0)) {
254 foreach (var item in App.BaseObjectPropertyList) {
255 if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
256 properties.Push(new JsonPropertyInfo(item));
257 }
258 }
259 }
260 foreach (var item in eobject.type.properties) {
261 if ((item.required.Length > 0) && (item.required[0] != "NONE")) {
262 properties.Push(new JsonPropertyInfo(item));
263 }
264 }
265 eobject.properties = properties;
266 var view = Views.MainMenuView.NewBlocklyView(eobject.identifier);
267 var workspace = new EObjectWorkspace(view, eobject);
268 view.BlockCreated += workspace.OnBlockCreated;
269 view.BlockDeleted += workspace.OnBlockDeleted;
270 view.BlockChanged += workspace.OnBlockChanged;
271 view.BlockMoveed += workspace.OnBlockMoveed;
272 callback(workspace);
273 }
274
275 internal static void RemoveItem(IClassWorkspace item)
276 {
277 Views.MainMenuView.RemoveEObjectWorkspace(item);
278 }
279
280 internal static void Write(string text)
281 {
282 Term?.write(text.Replace("\n", "\r\n"));
283 }
284
285 internal static void WriteLine(string text)
286 {
287 Term?.write(text.Replace("\n", "\r\n") + "\r\n");
288 }
289 }
290
291 public interface IClassWorkspace : IModel
292 {
293 Workspace Workspace { get; }
294 BlocklyView View { get; }
295 Ruby RubyCode { get; }
296 string GetImageUrl();
297 bool IsPreset();
298 string ToCode(string filename);
299 void Activate();
300 void Inactivate();
301 void ReloadToolbox(HTMLElement toolbox);
302 void OpenModifyView(Action<bool> callback);
303 string Template(string template);
304 }
305
306 public class Collections
307 {
308 [Name(false)]
309 internal static ENodeWorkspace LocalNode;
310 [Name(false)]
311 internal static Collection<IClassWorkspace> ClassWorkspaces;
312 [Name(false)]
313 internal static EcnlTaskWorkspace EcnlTaskWorkspace;
314 [Name(false)]
315 internal static MainLoopWorkspace MainLoopWorkspace;
316 }
317
318 public class Views
319 {
320 [Name(false)]
321 internal static MainMenuView MainMenuView;
322 [Name(false)]
323 internal static ClassSelectorView ClassSelectorView;
324 [Name(false)]
325 internal static EObjectModalView EObjectModalView;
326 }
327
328 public class HexDump
329 {
330 string text;
331
332 public HexDump(Uint8Array bytes, int width)
333 {
334 var sb = new string[0];
335
336 for (int index = 0; index < bytes.Length; index += width) {
337 sb.Push(String.Format("{0:X4} : ", index)
338 + BinBump(bytes, index, width)
339 + AsciiDump(bytes, index, width));
340 }
341
342 text = sb.Join("\n");
343 }
344
345 private string BinBump(Uint8Array bytes, int offset, int width)
346 {
347 var sb = new System.Text.StringBuilder();
348
349 for (int index = 0; index < width; index++) {
350 if (index + offset < bytes.Length) {
351 sb.AppendFormat("{0:X2} ", bytes[index + offset]);
352 }
353 else {
354 sb.Append(" ");
355 }
356 }
357
358 return sb.ToString();
359 }
360
361 private string AsciiDump(Uint8Array bytes, int index, int width)
362 {
363 var sb = "";
364
365 if (index < bytes.Length) {
366 width = System.Math.Min(width, bytes.Length - index);
367
368 sb += ": ";
369 for (int i = 0; i < width; i++) {
370 byte b = bytes[i + index];
371 if (b < 0x20)
372 sb += "\x1B[1;3;31m" + String.FromCharCode(b + 0x40) + "\x1B[0m";
373 else
374 sb += String.FromCharCode(b);
375 }
376 }
377 else {
378 sb += ": ";
379 }
380
381 return sb;
382 }
383
384 public override string ToString()
385 {
386 return text;
387 }
388 }
389}
Note: See TracBrowser for help on using the repository browser.